├── .dockerignore ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── task.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yaml └── workflows │ ├── ci.yaml │ ├── codeql.yml │ ├── image.yaml │ └── semantic-pr.yaml ├── .gitignore ├── .golangci.yaml ├── .goreleaser.yaml ├── .mockery.yaml ├── .readthedocs.yaml ├── CODEOWNERS ├── Dockerfile.agent ├── Dockerfile.principal ├── LICENSE ├── Makefile ├── OWNERS ├── README.md ├── ROADMAP.md ├── SECURITY.md ├── VERSION ├── agent ├── agent.go ├── agent_test.go ├── connection.go ├── connection_test.go ├── filters.go ├── inbound.go ├── inbound_test.go ├── options.go ├── outbound.go ├── outbound_test.go ├── resource.go └── resource_test.go ├── cmd ├── agent │ └── main.go ├── cmd │ ├── fatal.go │ ├── kube.go │ ├── log.go │ ├── validators.go │ └── version.go ├── cmdutil │ ├── fatal.go │ ├── kube.go │ ├── log.go │ ├── term.go │ ├── text.go │ ├── text_test.go │ ├── validators.go │ └── version.go ├── ctl │ ├── agent.go │ ├── ca.go │ └── main.go └── principal │ └── main.go ├── docs ├── CONTRIBUTING.md ├── assets │ ├── 01-architecture.png │ ├── 02-integration-autonomous.png │ ├── 02-integration-shared.png │ ├── extra.css │ ├── favicon.png │ ├── logo.png │ └── ocm-architecture.png ├── concepts │ ├── agent-modes │ │ ├── autonomous.md │ │ ├── index.md │ │ └── managed.md │ ├── architecture.md │ ├── argocd-integration.md │ └── components-terminology.md ├── configuration │ ├── agent │ │ └── index.md │ └── principal │ │ └── index.md ├── contributing │ ├── code.md │ ├── docs.md │ ├── index.md │ ├── local.md │ ├── prs.md │ └── review.md ├── faq │ └── index.md ├── features │ └── index.md ├── getting-started │ ├── index.md │ ├── kubernetes │ │ └── index.md │ ├── ocm-io │ │ └── index.md │ └── openshift │ │ └── index.md ├── hack │ ├── argocd-agent-with-operator.md │ ├── easyrsa.md │ └── quickstart.md ├── images │ └── argocd-agent-autonomous-architecture.drawio.png ├── index.md ├── operations │ ├── metrics.md │ └── profiling.md ├── requirements.txt └── technical │ ├── applications.md │ ├── appprojects.md │ └── general.md ├── go.mod ├── go.sum ├── hack ├── dev-env │ ├── .gitignore │ ├── Makefile │ ├── Procfile │ ├── Procfile.e2e │ ├── README.md │ ├── agent-autonomous │ │ ├── argocd-secret.yaml │ │ └── kustomization.yaml │ ├── agent-managed │ │ ├── argocd-cmd-params-cm.yaml │ │ ├── argocd-secret.yaml │ │ └── kustomization.yaml │ ├── apps │ │ ├── autonomous-guestbook.yaml │ │ └── managed-guestbook.yaml │ ├── clean-apps.sh │ ├── common │ │ ├── default-project.yaml │ │ ├── kustomization.yaml │ │ └── redis-secret.yaml │ ├── control-plane │ │ ├── appproject-default.yaml │ │ ├── argocd-cmd-params-cm.yaml │ │ ├── argocd-secret.yaml │ │ ├── kustomization.yaml │ │ ├── redis-service.yaml │ │ ├── repo-server-service.yaml │ │ └── server-service.yaml │ ├── create-agent-config.sh │ ├── gen-creds.sh │ ├── print-argo-admin-password.sh │ ├── resources │ │ ├── metallb-ipaddresspool.yaml │ │ ├── scc-anyuid-seccomp-netbind.yaml │ │ └── vcluster.yaml │ ├── reverse-tunnel │ │ ├── README.md │ │ ├── client │ │ │ ├── client.toml │ │ │ └── run.sh │ │ ├── rathole-image │ │ │ ├── artifacts │ │ │ │ └── Dockerfile.new │ │ │ └── build-and-push.sh │ │ ├── server │ │ │ ├── deployment.yaml │ │ │ ├── kustomization.yaml │ │ │ ├── server.toml │ │ │ ├── service-internal.yaml │ │ │ └── service.yaml │ │ └── setup.sh │ ├── setup-vcluster-env.sh │ ├── start-agent-autonomous.sh │ ├── start-agent-managed.sh │ └── start-principal.sh ├── generate-proto.sh ├── install │ ├── install-codegen-go-tools.sh │ └── install-protoc.sh ├── profile.sh ├── release.sh ├── test.sh ├── users.sh └── vscode │ └── launch.json ├── install └── kubernetes │ ├── agent │ ├── agent-deployment.yaml │ ├── agent-metrics-service.yaml │ ├── agent-params-cm.yaml │ ├── agent-role.yaml │ ├── agent-rolebinding.yaml │ ├── agent-sa.yaml │ └── kustomization.yaml │ ├── base │ └── deployment │ │ └── kustomization.yaml │ └── principal │ ├── kustomization.yaml │ ├── principal-clusterrole.yaml │ ├── principal-clusterrolebinding.yaml │ ├── principal-deployment.yaml │ ├── principal-metrics-service.yaml │ ├── principal-params-cm.yaml │ ├── principal-role.yaml │ ├── principal-rolebinding.yaml │ ├── principal-sa.yaml │ ├── principal-service.yaml │ ├── principal-tls-secret.yaml │ └── principal-userpass-secret.yaml ├── internal ├── argocd │ └── cluster │ │ ├── cluster.go │ │ ├── cluster_test.go │ │ ├── conversion.go │ │ ├── informer.go │ │ ├── informer_test.go │ │ ├── manager.go │ │ ├── manager_test.go │ │ ├── mapping.go │ │ └── mapping_test.go ├── auth │ ├── interface.go │ ├── methods.go │ ├── methods_test.go │ ├── mocks │ │ └── Method.go │ ├── mtls │ │ ├── mtls.go │ │ └── mtls_test.go │ ├── userpass │ │ ├── testdata │ │ │ ├── generate-testdata.sh │ │ │ ├── userdb-good.txt │ │ │ └── userdb-partial.txt │ │ ├── userpass.go │ │ └── userpass_test.go │ └── util.go ├── backend │ ├── interface.go │ ├── kubernetes │ │ ├── application │ │ │ ├── kubernetes.go │ │ │ └── kubernetes_test.go │ │ ├── appproject │ │ │ ├── kubernetes.go │ │ │ └── kubernetes_test.go │ │ └── namespace │ │ │ └── kubernetes.go │ └── mocks │ │ ├── AppProject.go │ │ ├── Application.go │ │ └── Namespace.go ├── checkpoint │ ├── checkpoint.go │ └── checkpoint_test.go ├── clock │ ├── clock.go │ └── clock_test.go ├── config │ └── constants.go ├── env │ ├── env.go │ └── env_test.go ├── event │ ├── event.go │ └── event_test.go ├── filter │ ├── filter.go │ └── filter_test.go ├── grpcutil │ ├── address.go │ ├── address_test.go │ ├── errors.go │ └── errors_test.go ├── informer │ ├── informer.go │ ├── informer_test.go │ ├── options.go │ └── options_test.go ├── issuer │ ├── issuer.go │ ├── jwt.go │ ├── jwt_test.go │ └── mocks │ │ ├── Claims.go │ │ ├── Issuer.go │ │ └── JwtIssuerOption.go ├── kube │ └── client.go ├── labels │ ├── parser.go │ └── parser_test.go ├── manager │ ├── application │ │ ├── application.go │ │ ├── application_test.go │ │ └── state.go │ ├── appproject │ │ ├── appproject.go │ │ ├── appproject_test.go │ │ └── state.go │ ├── manager.go │ └── manager_test.go ├── metrics │ ├── metrics.go │ ├── server.go │ └── server_test.go ├── namedlock │ ├── namelock.go │ └── namelock_test.go ├── queue │ ├── mocks │ │ └── QueuePair.go │ ├── queue.go │ └── queue_test.go ├── resources │ ├── resources.go │ └── resources_test.go ├── resync │ ├── resync.go │ └── resync_test.go ├── session │ ├── session.go │ └── session_test.go ├── tlsutil │ ├── generate.go │ ├── generate_test.go │ ├── kubernetes.go │ ├── kubernetes_test.go │ ├── testdata │ │ ├── 001_test_cert.pem │ │ └── 001_test_key.pem │ ├── tlsutil.go │ └── tlsutil_test.go └── version │ ├── version.go │ └── version_test.go ├── mkdocs.yml ├── pkg ├── api │ └── grpc │ │ ├── authapi │ │ ├── auth.pb.go │ │ └── auth_grpc.pb.go │ │ ├── eventstreamapi │ │ ├── eventstream.pb.go │ │ └── eventstream_grpc.pb.go │ │ └── versionapi │ │ ├── version.pb.go │ │ └── version_grpc.pb.go ├── client │ ├── remote.go │ └── remote_test.go └── types │ ├── types.go │ └── types_test.go ├── principal ├── apis │ ├── auth │ │ ├── auth.go │ │ ├── auth.proto │ │ ├── auth_test.go │ │ └── options.go │ ├── eventstream │ │ ├── eventstream.go │ │ ├── eventstream.proto │ │ ├── eventstream_test.go │ │ └── mock │ │ │ └── mock.go │ └── version │ │ ├── version.go │ │ ├── version.proto │ │ └── version_test.go ├── auth.go ├── callbacks.go ├── event.go ├── event_test.go ├── listen.go ├── listen_test.go ├── logging.go ├── mocks │ ├── Application.go │ ├── RecvHook.go │ ├── SendHook.go │ └── ServerOption.go ├── options.go ├── options_test.go ├── resource.go ├── resource_test.go ├── resourceproxy │ ├── options.go │ ├── params.go │ ├── params_test.go │ ├── resourceproxy.go │ ├── resourceproxy_test.go │ ├── tracking.go │ └── tracking_test.go ├── server.go └── server_test.go ├── proto ├── README └── google │ ├── api │ ├── BUILD.bazel │ ├── README.md │ ├── annotations.proto │ ├── apikeys │ │ ├── BUILD.bazel │ │ └── v2 │ │ │ ├── BUILD.bazel │ │ │ ├── apikeys.proto │ │ │ ├── apikeys_grpc_service_config.json │ │ │ ├── apikeys_v2.yaml │ │ │ └── resources.proto │ ├── auth.proto │ ├── backend.proto │ ├── billing.proto │ ├── client.proto │ ├── config_change.proto │ ├── consumer.proto │ ├── context.proto │ ├── control.proto │ ├── distribution.proto │ ├── documentation.proto │ ├── endpoint.proto │ ├── error_reason.proto │ ├── expr │ │ ├── BUILD.bazel │ │ ├── cel.yaml │ │ ├── conformance │ │ │ └── v1alpha1 │ │ │ │ ├── BUILD.bazel │ │ │ │ └── conformance_service.proto │ │ ├── v1alpha1 │ │ │ ├── BUILD.bazel │ │ │ ├── checked.proto │ │ │ ├── eval.proto │ │ │ ├── explain.proto │ │ │ ├── syntax.proto │ │ │ └── value.proto │ │ └── v1beta1 │ │ │ ├── BUILD.bazel │ │ │ ├── decl.proto │ │ │ ├── eval.proto │ │ │ ├── expr.proto │ │ │ ├── source.proto │ │ │ └── value.proto │ ├── field_behavior.proto │ ├── http.proto │ ├── httpbody.proto │ ├── label.proto │ ├── launch_stage.proto │ ├── log.proto │ ├── logging.proto │ ├── metric.proto │ ├── monitored_resource.proto │ ├── monitoring.proto │ ├── policy.proto │ ├── quota.proto │ ├── resource.proto │ ├── routing.proto │ ├── service.proto │ ├── serviceconfig.yaml │ ├── servicecontrol │ │ ├── BUILD.bazel │ │ ├── README.md │ │ ├── v1 │ │ │ ├── BUILD.bazel │ │ │ ├── check_error.proto │ │ │ ├── distribution.proto │ │ │ ├── http_request.proto │ │ │ ├── log_entry.proto │ │ │ ├── metric_value.proto │ │ │ ├── operation.proto │ │ │ ├── quota_controller.proto │ │ │ ├── service_controller.proto │ │ │ ├── servicecontrol.yaml │ │ │ └── servicecontrol_grpc_service_config.json │ │ └── v2 │ │ │ ├── BUILD.bazel │ │ │ ├── service_controller.proto │ │ │ ├── servicecontrol.yaml │ │ │ └── servicecontrol_grpc_service_config.json │ ├── servicemanagement │ │ ├── BUILD.bazel │ │ ├── README.md │ │ └── v1 │ │ │ ├── BUILD.bazel │ │ │ ├── resources.proto │ │ │ ├── servicemanagement_gapic.yaml │ │ │ ├── servicemanagement_grpc_service_config.json │ │ │ ├── servicemanagement_v1.yaml │ │ │ └── servicemanager.proto │ ├── serviceusage │ │ ├── BUILD.bazel │ │ ├── v1 │ │ │ ├── BUILD.bazel │ │ │ ├── resources.proto │ │ │ ├── serviceusage.proto │ │ │ ├── serviceusage_grpc_service_config.json │ │ │ └── serviceusage_v1.yaml │ │ └── v1beta1 │ │ │ ├── BUILD.bazel │ │ │ ├── resources.proto │ │ │ ├── serviceusage.proto │ │ │ ├── serviceusage_grpc_service_config.json │ │ │ └── serviceusage_v1beta1.yaml │ ├── source_info.proto │ ├── system_parameter.proto │ ├── usage.proto │ └── visibility.proto │ └── protobuf │ ├── LICENSE │ ├── any.proto │ ├── api.proto │ ├── compiler │ └── plugin.proto │ ├── descriptor.proto │ ├── duration.proto │ ├── empty.proto │ ├── field_mask.proto │ ├── source_context.proto │ ├── struct.proto │ ├── timestamp.proto │ ├── type.proto │ └── wrappers.proto └── test ├── e2e └── e2e_test.go ├── e2e2 ├── README.md ├── basic_test.go ├── clusterinfo_test.go ├── fixture │ ├── argoclient.go │ ├── argosync.go │ ├── cluster.go │ ├── fixture.go │ ├── goreman.go │ ├── kubeclient.go │ ├── kubeconfig.go │ └── toxyproxy.go ├── fixture_test.go ├── resync_test.go ├── rp_test.go └── sync_test.go ├── fake ├── kube │ └── kubernetes.go ├── server.go └── testcerts │ └── testcerts.go ├── mocks └── k8s-workqueue │ └── TypedRateLimitingInterface.go ├── proxy └── proxy.go ├── run-e2e.sh └── testutil └── file.go /.dockerignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | vendor/ 3 | dist/ 4 | build/bin 5 | Dockerfile.agent 6 | Dockerfile.principal 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: 'bug' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **Steps to reproduce the behaviour** 14 | 1. (...) 15 | 2. (...) 16 | 3. (...) 17 | 18 | **Expected behavior** 19 | A clear and concise description of what you expected to happen. 20 | 21 | **Additional context** 22 | Add any other context about the problem here. 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: 'enhancement' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/task.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Task 🔧 3 | about: Internal things, technical debt, and to-do tasks to be performed. 4 | title: '' 5 | labels: 'kind/task' 6 | assignees: '' 7 | 8 | --- 9 | ### Is your task related to a problem? Please describe. 10 | 11 | 12 | ### Describe the solution you'd like 13 | 14 | 15 | ### Describe alternatives you've considered 16 | 17 | 18 | ### Additional context 19 | 20 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **What does this PR do / why we need it**: 2 | 3 | **Which issue(s) this PR fixes**: 4 | 5 | Fixes #? 6 | 7 | **How to test changes / Special notes to the reviewer**: 8 | 9 | 10 | **Checklist** 11 | 12 | * [ ] Documentation update is required by this PR (and has been updated) OR no documentation update is required. 13 | 14 | -------------------------------------------------------------------------------- /.github/dependabot.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "gomod" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | ignore: 8 | - dependency-name: k8s.io/* 9 | 10 | -------------------------------------------------------------------------------- /.github/workflows/semantic-pr.yaml: -------------------------------------------------------------------------------- 1 | name: "Lint PR" 2 | 3 | on: 4 | pull_request_target: 5 | types: 6 | - opened 7 | - edited 8 | - synchronize 9 | - reopened 10 | 11 | jobs: 12 | main: 13 | name: Validate PR title 14 | runs-on: ubuntu-latest 15 | permissions: 16 | pull-requests: read 17 | steps: 18 | - uses: amannn/action-semantic-pull-request@0723387faaf9b38adef4775cd42cfd5155ed6017 # v5.5.3 19 | env: 20 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | test/out 3 | test/profile 4 | vendor/ 5 | .vscode 6 | build/ 7 | -------------------------------------------------------------------------------- /.golangci.yaml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | run: 3 | tests: false 4 | linters: 5 | enable: 6 | - misspell 7 | exclusions: 8 | generated: lax 9 | presets: 10 | - comments 11 | - common-false-positives 12 | - legacy 13 | - std-error-handling 14 | paths: 15 | - third_party$ 16 | - builtin$ 17 | - examples$ 18 | settings: 19 | staticcheck: 20 | checks: 21 | - all 22 | issues: 23 | max-issues-per-linter: 0 24 | max-same-issues: 0 25 | formatters: 26 | enable: 27 | - gofmt 28 | - goimports 29 | exclusions: 30 | generated: lax 31 | paths: 32 | - third_party$ 33 | - builtin$ 34 | - examples$ 35 | -------------------------------------------------------------------------------- /.goreleaser.yaml: -------------------------------------------------------------------------------- 1 | version: 1 2 | 3 | project_name: argocd-agent 4 | 5 | before: 6 | hooks: 7 | - go mod tidy 8 | - ./hack/test.sh 9 | 10 | builds: 11 | - id: agent 12 | env: 13 | - CGO_ENABLED=0 14 | goos: 15 | - linux 16 | binary: argocd-agent-agent 17 | ldflags: "-extldflags=-static -X github.com/argoproj-labs/argocd-agent/internal/version.version={{.Version}}" 18 | goarch: 19 | - amd64 20 | - arm64 21 | - ppc64le 22 | - s390x 23 | main: './cmd/agent/main.go' 24 | - id: principal 25 | env: 26 | - CGO_ENABLED=0 27 | goos: 28 | - linux 29 | binary: argocd-agent-principal 30 | ldflags: "-extldflags=-static -X github.com/argoproj-labs/argocd-agent/internal/version.version={{.Version}}" 31 | goarch: 32 | - amd64 33 | - arm64 34 | - ppc64le 35 | - s390x 36 | main: './cmd/principal/main.go' 37 | 38 | 39 | archives: 40 | - format: tar.gz 41 | # this name template makes the OS and Arch compatible with the results of `uname`. 42 | name_template: >- 43 | {{ .ProjectName }}_ 44 | {{- title .Os }}_ 45 | {{- if eq .Arch "amd64" }}x86_64 46 | {{- else if eq .Arch "386" }}i386 47 | {{- else }}{{ .Arch }}{{ end }} 48 | {{- if .Arm }}v{{ .Arm }}{{ end }} 49 | # use zip for windows archives 50 | format_overrides: 51 | - goos: windows 52 | format: zip 53 | 54 | changelog: 55 | sort: asc 56 | filters: 57 | exclude: 58 | - "^docs:" 59 | - "^test:" 60 | -------------------------------------------------------------------------------- /.mockery.yaml: -------------------------------------------------------------------------------- 1 | with-expecter: true 2 | all: true 3 | dir: "{{.InterfaceDir}}/mocks" 4 | mockname: "{{.InterfaceName}}" 5 | outpkg: "mocks" 6 | filename: "{{.InterfaceName}}.go" 7 | packages: 8 | github.com/argoproj-labs/argocd-agent/internal/auth: 9 | github.com/argoproj-labs/argocd-agent/internal/queue: 10 | github.com/argoproj-labs/argocd-agent/internal/issuer: 11 | github.com/argoproj-labs/argocd-agent/internal/backend: 12 | k8s.io/client-go/util/workqueue: 13 | config: 14 | dir: "test/mocks/k8s-workqueue" 15 | all: false 16 | interfaces: 17 | TypedRateLimitingInterface: 18 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # Read the Docs configuration file for MkDocs projects 2 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 3 | 4 | # Required 5 | version: 2 6 | 7 | # Set the version of Python and other tools you might need 8 | build: 9 | os: ubuntu-22.04 10 | tools: 11 | python: "3.12" 12 | 13 | mkdocs: 14 | configuration: mkdocs.yml 15 | 16 | # Optionally declare the Python requirements required to build your docs 17 | python: 18 | install: 19 | - requirements: docs/requirements.txt 20 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # All 2 | * @jannfis @jgwest @ishitasequeira @chetan-rns 3 | -------------------------------------------------------------------------------- /Dockerfile.agent: -------------------------------------------------------------------------------- 1 | FROM docker.io/library/golang:1.23 AS builder 2 | WORKDIR /src 3 | COPY . . 4 | RUN make agent 5 | 6 | FROM docker.io/library/alpine:latest 7 | 8 | COPY --from=builder /src/dist/argocd-agent-agent /bin/argocd-agent-agent 9 | USER 999 10 | ENTRYPOINT ["/bin/argocd-agent-agent"] 11 | -------------------------------------------------------------------------------- /Dockerfile.principal: -------------------------------------------------------------------------------- 1 | FROM docker.io/library/golang:1.23 AS builder 2 | WORKDIR /src 3 | COPY . . 4 | RUN make principal 5 | 6 | FROM docker.io/library/alpine:latest 7 | 8 | COPY --from=builder /src/dist/argocd-agent-principal /bin/argocd-agent-principal 9 | USER 999 10 | ENTRYPOINT ["/bin/argocd-agent-principal"] 11 | -------------------------------------------------------------------------------- /OWNERS: -------------------------------------------------------------------------------- 1 | owners: 2 | - jannfis 3 | - jgwest 4 | - ishitasequeira 5 | 6 | approvers: 7 | - jannfis 8 | - jgwest 9 | - ishitasequeira 10 | - chetan-rns 11 | 12 | -------------------------------------------------------------------------------- /ROADMAP.md: -------------------------------------------------------------------------------- 1 | # argocd-agent's roadmap 2 | 3 | The following is a list of things and items we want to support in the future. Here, the future may mean anything between short term, mid term and long term. We have not yet decided on timelines for the items. 4 | 5 | Please note: 6 | * Not all of these items are scoped for the v1.0 release. 7 | * This is a living list. Items may be added and removed without further notice. 8 | 9 | ## Docs and testing 10 | 11 | * Docs, docs, docs 12 | * Proper end-to-end test suite 13 | 14 | ## Authentication and authorization 15 | 16 | * SPIFFE/SPIRE authentication method to support mutual, zero-trust authentication between the control plane and the agents 17 | * mTLS between the agent and the control plane (right now, only the server verifies its identity using TLS) 18 | 19 | ## Usability 20 | 21 | * Provide a CLI to automate bootstraping an agent on a cluster and to register an agent with the control plane 22 | * Ability to sync ApplicationSet and AppProject resources from the control plane to the agent 23 | 24 | ## Scalability 25 | 26 | * Make the principal elastic and highly available, i.e. support mechanisms such as HPA 27 | * Provide Application backends other than the Kubernetes API (for example, a database or a more scalable key-value store) to support more than a couple of thousands of clusters 28 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | The `argocd-agent` project is not ready for production yet. However, we do appreciate people fixing security issues in the code. 4 | 5 | At this point in time, that means prior to GA or close to that, we will neither issue CVEs nor security advisories for discovered and fixed security issues. However, we will mention issues in release notes and we are happy to credit people who helped out. 6 | 7 | ## Supported Versions 8 | 9 | We do not have a support matrix yet. We do plan to follow the support matrix of Argo CD. 10 | 11 | ## Reporting a Vulnerability 12 | 13 | Right now, please feel free to raise a GitHub issue and/or a PR with a fix for any vulnerability you come across. 14 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.0.1-alpha 2 | -------------------------------------------------------------------------------- /agent/connection_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package agent 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/argoproj-labs/argocd-agent/internal/event" 21 | "github.com/argoproj-labs/argocd-agent/pkg/types" 22 | "github.com/stretchr/testify/assert" 23 | "k8s.io/client-go/rest" 24 | ) 25 | 26 | func TestResyncOnStart(t *testing.T) { 27 | a := newAgent(t) 28 | a.emitter = event.NewEventSource("test") 29 | a.kubeClient.RestConfig = &rest.Config{} 30 | logCtx := log() 31 | 32 | t.Run("should return if the agent has already been synced", func(t *testing.T) { 33 | a.resyncedOnStart = true 34 | err := a.resyncOnStart(logCtx) 35 | assert.Nil(t, err) 36 | 37 | sendQ := a.queues.SendQ(defaultQueueName) 38 | assert.Zero(t, sendQ.Len()) 39 | }) 40 | 41 | t.Run("send resource resync request in autonomous mode", func(t *testing.T) { 42 | a.resyncedOnStart = false 43 | a.mode = types.AgentModeAutonomous 44 | err := a.resyncOnStart(logCtx) 45 | assert.Nil(t, err) 46 | 47 | sendQ := a.queues.SendQ(defaultQueueName) 48 | assert.Equal(t, 1, sendQ.Len()) 49 | 50 | ev, shutdown := sendQ.Get() 51 | assert.False(t, shutdown) 52 | 53 | assert.Equal(t, event.EventRequestResourceResync.String(), ev.Type()) 54 | assert.True(t, a.resyncedOnStart) 55 | }) 56 | 57 | t.Run("send synced resource list request in managed mode", func(t *testing.T) { 58 | a.resyncedOnStart = false 59 | a.mode = types.AgentModeManaged 60 | err := a.resyncOnStart(logCtx) 61 | assert.Nil(t, err) 62 | 63 | sendQ := a.queues.SendQ(defaultQueueName) 64 | assert.Equal(t, 1, sendQ.Len()) 65 | 66 | ev, shutdown := sendQ.Get() 67 | assert.False(t, shutdown) 68 | 69 | assert.Equal(t, event.SyncedResourceList.String(), ev.Type()) 70 | assert.True(t, a.resyncedOnStart) 71 | }) 72 | } 73 | -------------------------------------------------------------------------------- /agent/filters.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package agent 16 | 17 | import ( 18 | "github.com/argoproj-labs/argocd-agent/internal/filter" 19 | "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" 20 | "github.com/argoproj/argo-cd/v2/util/glob" 21 | ) 22 | 23 | // DefaultAppFilterChain returns a FilterChain for Application resources. 24 | // This chain contains a set of default filters that the agent will 25 | // evaluate for every change. 26 | func (a *Agent) DefaultAppFilterChain() *filter.Chain[*v1alpha1.Application] { 27 | fc := &filter.Chain[*v1alpha1.Application]{} 28 | 29 | // Admit based on namespace of the application 30 | fc.AppendAdmitFilter(func(app *v1alpha1.Application) bool { 31 | if !glob.MatchStringInList(append([]string{a.namespace}, a.options.namespaces...), app.Namespace, glob.REGEXP) { 32 | log().Warnf("namespace not allowed: %s", app.QualifiedName()) 33 | return false 34 | } 35 | // if a.managedApps.IsManaged(app.QualifiedName()) { 36 | // log().Warnf("App is not managed: %s", app.QualifiedName()) 37 | // return false 38 | // } 39 | return true 40 | }) 41 | 42 | return fc 43 | } 44 | 45 | func (a *Agent) WithLabelFilter() { 46 | } 47 | -------------------------------------------------------------------------------- /agent/options.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package agent 16 | 17 | import ( 18 | "fmt" 19 | 20 | "github.com/argoproj-labs/argocd-agent/pkg/client" 21 | "github.com/argoproj-labs/argocd-agent/pkg/types" 22 | ) 23 | 24 | func WithAllowedNamespaces(namespaces ...string) AgentOption { 25 | return func(a *Agent) error { 26 | a.allowedNamespaces = namespaces 27 | return nil 28 | } 29 | } 30 | 31 | func WithRemote(remote *client.Remote) AgentOption { 32 | return func(a *Agent) error { 33 | a.remote = remote 34 | return nil 35 | } 36 | } 37 | 38 | func WithMode(mode string) AgentOption { 39 | return func(a *Agent) error { 40 | switch mode { 41 | case "autonomous": 42 | a.mode = types.AgentModeAutonomous 43 | case "managed": 44 | a.mode = types.AgentModeManaged 45 | default: 46 | a.mode = types.AgentModeUnknown 47 | return fmt.Errorf("unknown agent mode: %s. Must be one of: managed,autonomous", mode) 48 | } 49 | return nil 50 | } 51 | } 52 | 53 | func WithMetricsPort(port int) AgentOption { 54 | return func(o *Agent) error { 55 | if port > 0 && port < 32768 { 56 | o.options.metricsPort = port 57 | return nil 58 | } else { 59 | return fmt.Errorf("invalid port: %d", port) 60 | } 61 | } 62 | } 63 | 64 | func WithHealthzPort(port int) AgentOption { 65 | return func(o *Agent) error { 66 | if port > 0 && port < 32768 { 67 | o.options.healthzPort = port 68 | return nil 69 | } else { 70 | return fmt.Errorf("invalid port: %d", port) 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /cmd/cmd/fatal.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package cmd 16 | 17 | import ( 18 | "fmt" 19 | "os" 20 | ) 21 | 22 | func Error(msg string, args ...interface{}) { 23 | } 24 | 25 | func Fatal(msg string, args ...interface{}) { 26 | FatalWithExitCode(1, msg, args...) 27 | } 28 | 29 | func FatalWithExitCode(code int, msg string, args ...interface{}) { 30 | fmt.Fprintf(os.Stderr, "[FATAL]: ") 31 | fmt.Fprintf(os.Stderr, msg, args...) 32 | fmt.Fprintf(os.Stderr, "\n") 33 | os.Exit(code) 34 | } 35 | -------------------------------------------------------------------------------- /cmd/cmd/kube.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package cmd 16 | 17 | import ( 18 | "context" 19 | "fmt" 20 | "path/filepath" 21 | 22 | "github.com/argoproj-labs/argocd-agent/internal/kube" 23 | ) 24 | 25 | func GetKubeConfig(ctx context.Context, namespace string, kubeConfig string, kubecontext string) (*kube.KubernetesClient, error) { 26 | var fullKubeConfigPath string 27 | var kubeClient *kube.KubernetesClient 28 | var err error 29 | 30 | if kubeConfig != "" { 31 | fullKubeConfigPath, err = filepath.Abs(kubeConfig) 32 | if err != nil { 33 | return nil, fmt.Errorf("cannot expand path %s: %v", kubeConfig, err) 34 | } 35 | } 36 | 37 | kubeClient, err = kube.NewKubernetesClientFromConfig(ctx, namespace, fullKubeConfigPath, kubecontext) 38 | if err != nil { 39 | return nil, err 40 | } 41 | 42 | return kubeClient, nil 43 | } 44 | -------------------------------------------------------------------------------- /cmd/cmd/validators.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package cmd 16 | 17 | import "fmt" 18 | 19 | func ValidPort(num int) error { 20 | if num < 0 || num > 65536 { 21 | return fmt.Errorf("%d: not a valid port number", num) 22 | } 23 | return nil 24 | } 25 | -------------------------------------------------------------------------------- /cmd/cmd/version.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package cmd 16 | 17 | import ( 18 | "fmt" 19 | 20 | "github.com/argoproj-labs/argocd-agent/internal/version" 21 | ) 22 | 23 | const ( 24 | VersionFormatText = "text" 25 | VersionFormatJSONCompact = "json" 26 | VersionFormatJSONIndent = "json-indent" 27 | VersionFormatYAML = "yaml" 28 | ) 29 | 30 | func PrintVersion(v *version.Version, format string) { 31 | switch format { 32 | case VersionFormatText: 33 | fmt.Println(v.Version()) 34 | case VersionFormatJSONCompact: 35 | fmt.Println(v.JSON(false)) 36 | case VersionFormatJSONIndent: 37 | fmt.Println(v.JSON(true)) 38 | case VersionFormatYAML: 39 | fmt.Println(v.YAML()) 40 | default: 41 | fmt.Printf("Warning: Unknown version format '%s', falling back to %s", format, VersionFormatText) 42 | fmt.Println(v.Version()) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /cmd/cmdutil/fatal.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package cmdutil 16 | 17 | import ( 18 | "fmt" 19 | "os" 20 | ) 21 | 22 | func Error(msg string, args ...interface{}) { 23 | } 24 | 25 | func Fatal(msg string, args ...interface{}) { 26 | FatalWithExitCode(1, msg, args...) 27 | } 28 | 29 | func FatalWithExitCode(code int, msg string, args ...interface{}) { 30 | fmt.Fprintf(os.Stderr, "[FATAL]: ") 31 | fmt.Fprintf(os.Stderr, msg, args...) 32 | fmt.Fprintf(os.Stderr, "\n") 33 | os.Exit(code) 34 | } 35 | -------------------------------------------------------------------------------- /cmd/cmdutil/kube.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package cmdutil 16 | 17 | import ( 18 | "context" 19 | "fmt" 20 | "path/filepath" 21 | 22 | "github.com/argoproj-labs/argocd-agent/internal/kube" 23 | ) 24 | 25 | func GetKubeConfig(ctx context.Context, namespace string, kubeConfig string, kubecontext string) (*kube.KubernetesClient, error) { 26 | var fullKubeConfigPath string 27 | var kubeClient *kube.KubernetesClient 28 | var err error 29 | 30 | if kubeConfig != "" { 31 | fullKubeConfigPath, err = filepath.Abs(kubeConfig) 32 | if err != nil { 33 | return nil, fmt.Errorf("cannot expand path %s: %v", kubeConfig, err) 34 | } 35 | } 36 | 37 | kubeClient, err = kube.NewKubernetesClientFromConfig(ctx, namespace, fullKubeConfigPath, kubecontext) 38 | if err != nil { 39 | return nil, err 40 | } 41 | 42 | return kubeClient, nil 43 | } 44 | -------------------------------------------------------------------------------- /cmd/cmdutil/term.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package cmdutil 16 | 17 | import ( 18 | "bufio" 19 | "fmt" 20 | "os" 21 | "strings" 22 | ) 23 | 24 | // ReadFromTerm displays a prompt and reads user input from the terminal 25 | // 26 | // It writes prompt to stdout and then reads user input from stdin. 27 | // 28 | // If isValid is nil, the string entered by the user will be immediately 29 | // returned. Otherwise, if isValid is non-nil, it will determine the 30 | // validity of the user's input. 31 | // 32 | // maxRetries specifies how often the user is allowed to retry their input. 33 | // If maxRetries is -1, the function will only return once the input is 34 | // considered valid, otherwise the function will return an error after 35 | // maxRetries has been reached. 36 | func ReadFromTerm(prompt string, maxRetries int, isValid func(s string) (valid bool)) (string, error) { 37 | tries := 0 38 | for { 39 | tries += 1 40 | fmt.Printf("%s: ", prompt) 41 | reader := bufio.NewReader(os.Stdin) 42 | val, err := reader.ReadString('\n') 43 | if err != nil { 44 | return "", err 45 | } 46 | val = strings.TrimSuffix(val, "\n") 47 | if isValid != nil { 48 | if isValid(val) { 49 | return val, nil 50 | } else { 51 | if maxRetries == -1 { 52 | continue 53 | } else { 54 | if tries > maxRetries { 55 | return "", fmt.Errorf("%s: invalid value", val) 56 | } 57 | } 58 | } 59 | } else { 60 | return val, nil 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /cmd/cmdutil/validators.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package cmdutil 16 | 17 | import "fmt" 18 | 19 | func ValidPort(num int) error { 20 | if num < 0 || num > 65536 { 21 | return fmt.Errorf("%d: not a valid port number", num) 22 | } 23 | return nil 24 | } 25 | -------------------------------------------------------------------------------- /cmd/cmdutil/version.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package cmdutil 16 | 17 | import ( 18 | "fmt" 19 | 20 | "github.com/argoproj-labs/argocd-agent/internal/version" 21 | ) 22 | 23 | const ( 24 | VersionFormatText = "text" 25 | VersionFormatJSONCompact = "json" 26 | VersionFormatJSONIndent = "json-indent" 27 | VersionFormatYAML = "yaml" 28 | ) 29 | 30 | func PrintVersion(v *version.Version, format string) { 31 | switch format { 32 | case VersionFormatText: 33 | fmt.Println(v.Version()) 34 | case VersionFormatJSONCompact: 35 | fmt.Println(v.JSON(false)) 36 | case VersionFormatJSONIndent: 37 | fmt.Println(v.JSON(true)) 38 | case VersionFormatYAML: 39 | fmt.Println(v.YAML()) 40 | default: 41 | fmt.Printf("Warning: Unknown version format '%s', falling back to %s", format, VersionFormatText) 42 | fmt.Println(v.Version()) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /docs/assets/01-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj-labs/argocd-agent/ea8f330e68feb5c72d7c2ca0e3426f22e83a7dd6/docs/assets/01-architecture.png -------------------------------------------------------------------------------- /docs/assets/02-integration-autonomous.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj-labs/argocd-agent/ea8f330e68feb5c72d7c2ca0e3426f22e83a7dd6/docs/assets/02-integration-autonomous.png -------------------------------------------------------------------------------- /docs/assets/02-integration-shared.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj-labs/argocd-agent/ea8f330e68feb5c72d7c2ca0e3426f22e83a7dd6/docs/assets/02-integration-shared.png -------------------------------------------------------------------------------- /docs/assets/extra.css: -------------------------------------------------------------------------------- 1 | .codehilite { 2 | background-color: hsla(0,0%,92.5%,.5); 3 | overflow: auto; 4 | -webkit-overflow-scrolling: touch; 5 | } 6 | 7 | .codehilite pre { 8 | background-color: transparent; 9 | padding: .525rem .6rem; 10 | } 11 | 12 | @media only screen and (min-width: 76.25em) { 13 | .md-main__inner { 14 | max-width: none; 15 | } 16 | .md-sidebar--primary { 17 | left: 0; 18 | } 19 | .md-sidebar--secondary { 20 | right: 0; 21 | margin-left: 0; 22 | -webkit-transform: none; 23 | transform: none; 24 | } 25 | } -------------------------------------------------------------------------------- /docs/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj-labs/argocd-agent/ea8f330e68feb5c72d7c2ca0e3426f22e83a7dd6/docs/assets/favicon.png -------------------------------------------------------------------------------- /docs/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj-labs/argocd-agent/ea8f330e68feb5c72d7c2ca0e3426f22e83a7dd6/docs/assets/logo.png -------------------------------------------------------------------------------- /docs/assets/ocm-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj-labs/argocd-agent/ea8f330e68feb5c72d7c2ca0e3426f22e83a7dd6/docs/assets/ocm-architecture.png -------------------------------------------------------------------------------- /docs/concepts/agent-modes/autonomous.md: -------------------------------------------------------------------------------- 1 | # Autonomous mode 2 | 3 | ## Overview of autonomous mode 4 | 5 | In *autonomous mode*, the workload cluster is wholly responsible for maintaining its own configuration. As opposed to the [managed mode](./managed.md), all configuration is first created on the workload cluster. The agent on this workload cluster will observe creation, modification and deletion of configuration and transmit them to the principal on the control plane cluster. 6 | 7 | The principal will then create, update or delete this configuration on the control plane cluster. Users can use the Argo CD UI, CLI or API to inspect the status of the configuration, but they are not able to perform changes to the configuration or delete it. 8 | 9 | ## Architectural considerations 10 | 11 | ## Why chose this mode 12 | 13 | ## Why not chose this mode -------------------------------------------------------------------------------- /docs/concepts/agent-modes/index.md: -------------------------------------------------------------------------------- 1 | # Agent modes 2 | 3 | The main purpose of the [agent](../components-terminology.md#agent) and the [principal](../components-terminology.md#principal) components is to keep configuration in sync between the [workload clusters](../components-terminology.md#workload-cluster) and the [control plane cluster](../components-terminology.md#control-plane-cluster). 4 | 5 | Each agent can operate in one of two distinct configuration modes: *managed* or *autonomous*. These modes define the general sync direction: From the workload cluster to the control plane cluster (*autonomous*), or from the control plane cluster to the workload cluster (*managed*). 6 | 7 | Please refer to the sub-chapters [Managed mode](./managed.md) and [Autonomous mode](./autonomous.md) for detailed information, architectural considerations and constraints to chose the mode most appropriate for your agents. -------------------------------------------------------------------------------- /docs/configuration/agent/index.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj-labs/argocd-agent/ea8f330e68feb5c72d7c2ca0e3426f22e83a7dd6/docs/configuration/agent/index.md -------------------------------------------------------------------------------- /docs/configuration/principal/index.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj-labs/argocd-agent/ea8f330e68feb5c72d7c2ca0e3426f22e83a7dd6/docs/configuration/principal/index.md -------------------------------------------------------------------------------- /docs/contributing/docs.md: -------------------------------------------------------------------------------- 1 | # Docs contributions 2 | 3 | There are only a couple of rules (or kind asks) for contributions to documentation: 4 | 5 | ## General 6 | 7 | * Please use a spell checker for contributions to the docs. We recommend [cspell](https://cspell.org/), which also has a plugin for VS Code. 8 | * Please be mindful of the language you use. 9 | 10 | ## Typo and grammar fixes 11 | 12 | We appreciate people submitting PRs to fix typos and grammar. 13 | 14 | ## Corrections 15 | 16 | Sometimes, the documentation is outdated, missing some important piece of information or plain wrong. Please feel free to submit PRs to correct these kind of bugs. 17 | 18 | ## Translations 19 | 20 | At this point in time, we do not have the capacity to handle translated documentation. We kindly ask to keep all submissions in the English language. -------------------------------------------------------------------------------- /docs/contributing/index.md: -------------------------------------------------------------------------------- 1 | # Contributing to argocd-agent 2 | 3 | ## Preface 4 | 5 | *argocd-agent* is a community-driven Open Source project, published under the liberal Apache-2.0 license. 6 | 7 | The project lives from the contributions of its community, and everybody is invited to contribute to it and to become part of its community. 8 | 9 | Contributions can come in many forms, for example, but not limited to: 10 | 11 | * Contributing code to implement new features or fix existing bugs, 12 | * Contributing documentation, so the project becomes easier to use for everyone, 13 | * Taking part in technical discussions, so the project becomes better, 14 | * Raising bug reports or feature requests 15 | * Review other people's PRs 16 | * Triage and participate in bug resolution 17 | * Engage with the community to discuss use cases, issues, etc 18 | 19 | To keep the project focused and on track, all contributions must adhere to a set of simple rules in order to be accepted. This ensures that everyone's time is used efficiently—both for potential contributors whose submissions may be rejected, and for reviewers and maintainers involved in the process. 20 | 21 | A pre-requisite for all existing and would-be contributors is to abide by the Argo project's code of conduct. In general that means: Please be kind to each other and keep things focused, constructive and on-topic. 22 | 23 | This chapter of the documentation will guide any contributor, whether brand new to the project or a seasoned veteran, to make a good contribution to the project. 24 | 25 | ## Connecting with the community 26 | 27 | Currently, the ways to connect with the community are: 28 | 29 | * Join the [#argo-cd-agent channel](https://cloud-native.slack.com/archives/C07L5SX6A9J) on the CNCF slack. If you don't have an account yet, you can easily request one for free [here](https://communityinviter.com/apps/cloud-native/cncf) 30 | * Open an [issue](https://github.com/argoproj-labs/argocd-agent/issues) or [discussion](https://github.com/argoproj-labs/argocd-agent/discussions) on our GitHub project page -------------------------------------------------------------------------------- /docs/contributing/local.md: -------------------------------------------------------------------------------- 1 | # Local development 2 | 3 | Since *argocd-agent* is a distributed system, i.e. requires components across *multiple* clusters to talk to each other, local development is a little bit more involved. -------------------------------------------------------------------------------- /docs/contributing/prs.md: -------------------------------------------------------------------------------- 1 | # Submitting PRs 2 | 3 | ## Preface 4 | 5 | Before submitting PRs, please make sure that: 6 | 7 | * You have read the code contribution guidelines, -------------------------------------------------------------------------------- /docs/contributing/review.md: -------------------------------------------------------------------------------- 1 | # Code reviewing guide -------------------------------------------------------------------------------- /docs/faq/index.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj-labs/argocd-agent/ea8f330e68feb5c72d7c2ca0e3426f22e83a7dd6/docs/faq/index.md -------------------------------------------------------------------------------- /docs/features/index.md: -------------------------------------------------------------------------------- 1 | # Features 2 | 3 | -------------------------------------------------------------------------------- /docs/getting-started/kubernetes/index.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj-labs/argocd-agent/ea8f330e68feb5c72d7c2ca0e3426f22e83a7dd6/docs/getting-started/kubernetes/index.md -------------------------------------------------------------------------------- /docs/getting-started/openshift/index.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj-labs/argocd-agent/ea8f330e68feb5c72d7c2ca0e3426f22e83a7dd6/docs/getting-started/openshift/index.md -------------------------------------------------------------------------------- /docs/hack/easyrsa.md: -------------------------------------------------------------------------------- 1 | # BYO-CA 2 | 3 | **NOTE:** This should not be considered official documentation. It should help you get going, but not spoon-feed you. You are expected to hack a little. If there's something wrong or missing in this docs, please feel free to submit a PR. 4 | 5 | ## Setting up the CA 6 | 7 | You can up a (non-production, transient) local certificate authority. We recommend using [EasyRSA](https://github.com/OpenVPN/easy-rsa/releases) for this purpose. 8 | 9 | To get started with EasyRSA: 10 | 11 | * Download the latest EasyRSA release 12 | * Extract the tar ball and enter the directory that was created (e.g. `EasyRSA-3.2.0`) 13 | 14 | Create a file `vars` in that directory, with the following contents: 15 | 16 | ``` 17 | set_var EASYRSA_PKI "$HOME/argo-agent-pki" 18 | 19 | set_var EASYRSA_REQ_COUNTRY "" 20 | set_var EASYRSA_REQ_PROVINCE "" 21 | set_var EASYRSA_REQ_CITY "" 22 | set_var EASYRSA_REQ_ORG "argocd-agent temporary" 23 | set_var EASYRSA_REQ_EMAIL "me@example.net" 24 | set_var EASYRSA_REQ_OU "argocd-agent" 25 | set_var EASYRSA_NO_PASS 1 26 | set_var EASYRSA_KEY_SIZE 4096 27 | ``` 28 | 29 | Still in this directory, run 30 | 31 | ``` 32 | ./easyrsa init-pki 33 | ``` 34 | 35 | This will create your `$HOME/argo-agent-pki` directory, which will be used to store all certificates and private keys. Then, run 36 | 37 | ``` 38 | ./easyrsa build-ca 39 | ``` 40 | 41 | to initialize the actual CA. 42 | 43 | ## Issuing the principal's certificates 44 | 45 | For the principal, a server certificate is required: 46 | 47 | ``` 48 | ./easysrsa --san build-server-full 49 | ``` 50 | 51 | It is important to figure out the right DNS names or IP addresses where your agents will access the principal. -------------------------------------------------------------------------------- /docs/images/argocd-agent-autonomous-architecture.drawio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj-labs/argocd-agent/ea8f330e68feb5c72d7c2ca0e3426f22e83a7dd6/docs/images/argocd-agent-autonomous-architecture.drawio.png -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | ## About argocd-agent 4 | 5 | The argocd-agent project seeks to enhance the multi-cluster functionality of the popular open-source GitOps tool Argo CD by introducing an agent-based architecture for managing resources across remote clusters. 6 | 7 | Its primary goals are to improve scalability and security as Argo CD expands beyond just a few clusters, while maintaining the features that users love: ease of use, centralized management, and observability. 8 | 9 | Although the argocd-agent project is still in its early stages and not yet stable, everyone is encouraged to try it out, test it, and contribute. 10 | 11 | ## About this documentation 12 | 13 | This documentation is work-in-progress. It might be outdated, missing important pieces, etc. Feel free to contribute to the documentation by updating, adding and correcting. 14 | 15 | We do expect good working knowledge of general Argo CD concepts, architecture and terminology from the reader. 16 | 17 | ## License 18 | 19 | The project and all of its code, documentation and assets is available under the Apache 2.0 license. 20 | 21 | ## Architecture 22 | 23 | Please have a look at the [Architecture](./concepts/architecture.md) docs. 24 | 25 | ## Contributing 26 | 27 | We welcome contributions from the community. In order to get started, please take a look at our contribution guide and documentation. -------------------------------------------------------------------------------- /docs/operations/profiling.md: -------------------------------------------------------------------------------- 1 | # Profiling 2 | The argocd-agent uses [pprof](https://github.com/google/pprof) for collecting profiling data. 3 | Profiling is by default disabled in both principal and agent and it will be enabled if `pprof-port` flag is set or environment variable `ARGOCD_PRINCIPAL_PPROF_PORT` in principal and `ARGOCD_AGENT_PPROF_PORT` in agent are set. 4 | 5 | The profiling data of principal can be seen at endpoint `http://localhost:6060/debug/pprof`, and for agent it is available at endpoint `http://localhost:6161/debug/pprof`, these would have list of endpoints for different type of profiling. 6 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | # Requirements for core 2 | jinja2~=3.0 3 | markdown~=3.2 4 | mkdocs~=1.6 5 | mkdocs-material~=9.6 6 | mkdocs-material-extensions~=1.3 7 | pygments~=2.16 8 | pymdown-extensions~=10.2 9 | 10 | # Requirements for plugins 11 | babel~=2.10 12 | colorama~=0.4 13 | paginate~=0.5 14 | regex>=2022.4 15 | requests~=2.26 -------------------------------------------------------------------------------- /docs/technical/applications.md: -------------------------------------------------------------------------------- 1 | # Synchronization of Applications 2 | 3 | ## Meta 4 | 5 | ||| 6 | |---------|------| 7 | |**Title**|Synchronization of Applications| 8 | |**Document status**|Work in Progress| 9 | |**Document author**|@jannfis| 10 | |**Implemented**|Partly| 11 | 12 | ## Abstract 13 | 14 | An `Application` is the at heart of Argo CD. In a nutshell, it maps one or more source(s) from Git (or another type of repository) to a destination cluster and specifies how to reconcile it. The majority of the reconciliation work is performed by Argo CD's application controller. 15 | 16 | There are generally two parts to an Application resource: 17 | 18 | * the `.spec` field, which holds user configurable reconciliation settings 19 | * the `.status` field, where the application controller records a variety of information regarding the reconciliation activity 20 | 21 | The status field is usually written to by the application controller, where as the spec can be written to by the user (e.g. through kubectl, Argo CD CLI or the web UI). 22 | 23 | A third field, `.operation`, can be written by both, the user and the controller. It is attached to the resource when a sync operation is requested, and removed from the resource once the operation is in progress. 24 | 25 | ## Requirements 26 | 27 | * An Application must only be reconciled on the target system, not on the control plane 28 | * Whenever an Application is transfered from the principal to the agent or vice versa, it must be ensured 29 | 30 | ## Assumptions 31 | 32 | ## Design 33 | 34 | ## Open questions 35 | 36 | ## Risks 37 | 38 | ## Further considerations 39 | -------------------------------------------------------------------------------- /docs/technical/general.md: -------------------------------------------------------------------------------- 1 | # General architectural considerations 2 | 3 | ## Meta 4 | 5 | ||| 6 | |---------|------| 7 | |**Title**|General architectural considerations| 8 | |**Document status**|Work in Progress| 9 | |**Document author**|@jannfis| 10 | |**Implemented**|No| 11 | 12 | ## Abstract 13 | 14 | This document describes some of the general architectural considerations and design goals within the `argocd-agent` code base. We'd like every contributor to read this document and assess their own contributions against the information laid out in this doc. 15 | 16 | ## Runtime 17 | 18 | ### Process termination, fatalities 19 | 20 | Program flow should not be terminated lightly. Instead, whenever you would terminate the program flow, it should be evaluated if the error situation is recoverable. For example, recoverable situations that are aborted in other programs usually are: 21 | 22 | * A TCP server cannot listen on a given address/port. 23 | 24 | ### Logging 25 | 26 | We make use of structured logging. As much information as possible should 27 | 28 | ## Networking -------------------------------------------------------------------------------- /hack/dev-env/.gitignore: -------------------------------------------------------------------------------- 1 | creds 2 | -------------------------------------------------------------------------------- /hack/dev-env/Makefile: -------------------------------------------------------------------------------- 1 | AUTONOMOUS_MODE?=false 2 | GOBIN=$(shell go env GOPATH)/bin 3 | 4 | .PHONY: start-local 5 | start-local: 6 | AUTONOMOUS_MODE=$(AUTONOMOUS_MODE) $(GOBIN)/goreman -set-ports=false -f Procfile start 7 | 8 | .PHONY: help 9 | help: 10 | @echo "Not yet, sorry." 11 | -------------------------------------------------------------------------------- /hack/dev-env/Procfile: -------------------------------------------------------------------------------- 1 | principal: ./start-principal.sh 2 | agent: sleep 15s && if [ "$AUTONOMOUS_MODE" = "true" ]; then ./start-agent-autonomous.sh; else ./start-agent-managed.sh; fi 3 | -------------------------------------------------------------------------------- /hack/dev-env/Procfile.e2e: -------------------------------------------------------------------------------- 1 | principal: hack/dev-env/start-principal.sh 2 | agent-managed: sleep 5s && hack/dev-env/start-agent-managed.sh 3 | agent-autonomous: sleep 5s && hack/dev-env/start-agent-autonomous.sh 4 | -------------------------------------------------------------------------------- /hack/dev-env/agent-autonomous/argocd-secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: argocd-secret 5 | -------------------------------------------------------------------------------- /hack/dev-env/agent-autonomous/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - ../argo-cd/manifests/crds 3 | - ../argo-cd/manifests/base/config 4 | - ../argo-cd/manifests/base/redis 5 | - ../argo-cd/manifests/base/repo-server 6 | - ../argo-cd/manifests/base/application-controller 7 | - ../argo-cd/manifests/cluster-rbac/application-controller 8 | - ../common 9 | 10 | images: 11 | - name: quay.io/argoproj/argocd 12 | newTag: LatestReleaseTag 13 | 14 | patches: 15 | - path: argocd-secret.yaml 16 | -------------------------------------------------------------------------------- /hack/dev-env/agent-managed/argocd-cmd-params-cm.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: argocd-cmd-params-cm 5 | data: 6 | repo.server: repo-server-address:8081 7 | redis.server: redis-server-address:6379 8 | application.namespaces: agent-managed -------------------------------------------------------------------------------- /hack/dev-env/agent-managed/argocd-secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: argocd-secret 5 | -------------------------------------------------------------------------------- /hack/dev-env/agent-managed/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - ../argo-cd/manifests/crds 3 | - ../argo-cd/manifests/base/config 4 | - ../argo-cd/manifests/base/redis 5 | - ../argo-cd/manifests/base/repo-server 6 | - ../argo-cd/manifests/base/application-controller 7 | - ../argo-cd/manifests/cluster-rbac/application-controller 8 | - ../common 9 | 10 | images: 11 | - name: quay.io/argoproj/argocd 12 | newTag: LatestReleaseTag 13 | 14 | patches: 15 | - path: argocd-cmd-params-cm.yaml 16 | - path: argocd-secret.yaml 17 | -------------------------------------------------------------------------------- /hack/dev-env/apps/autonomous-guestbook.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Application 3 | metadata: 4 | name: guestbook 5 | namespace: argocd 6 | spec: 7 | project: default 8 | source: 9 | repoURL: https://github.com/argoproj/argocd-example-apps 10 | targetRevision: HEAD 11 | path: kustomize-guestbook 12 | destination: 13 | server: https://kubernetes.default.svc 14 | namespace: guestbook 15 | syncPolicy: 16 | syncOptions: 17 | - CreateNamespace=true 18 | -------------------------------------------------------------------------------- /hack/dev-env/apps/managed-guestbook.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Application 3 | metadata: 4 | name: guestbook 5 | namespace: agent-managed 6 | spec: 7 | project: default 8 | source: 9 | repoURL: https://github.com/argoproj/argocd-example-apps 10 | targetRevision: HEAD 11 | path: kustomize-guestbook 12 | destination: 13 | server: https://kubernetes.default.svc 14 | namespace: guestbook 15 | syncPolicy: 16 | syncOptions: 17 | - CreateNamespace=true 18 | -------------------------------------------------------------------------------- /hack/dev-env/clean-apps.sh: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The argocd-agent Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Script to clean all apps in the demo environment. It will remove any resource 16 | # finalizer before deletion, so it might leave your workloads behind. 17 | for cluster in control-plane agent-managed agent-autonomous; do 18 | apps=$(kubectl --context vcluster-${cluster} get apps -A --no-headers -o go-template="{{range .items}}{{.metadata.namespace}} {{.metadata.name}}{{end}}") 19 | test -z "$apps" && continue 20 | OIFS="$IFS" 21 | while IFS= read -r app; do 22 | IFS=" " set -- $app 23 | namespace="$1" 24 | name="$2" 25 | echo "Patching $namespace/$name in vcluster-${cluster}" 26 | kubectl --context vcluster-${cluster} patch -n $namespace app $name -p '{"metadata":{"finalizers":null}}' --type=merge 27 | done < <(echo "$apps") 28 | echo "Deleting all apps in vcluster-${cluster}" 29 | kubectl --context vcluster-${cluster} delete apps --all-namespaces --all 30 | done 31 | -------------------------------------------------------------------------------- /hack/dev-env/common/default-project.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: AppProject 3 | metadata: 4 | name: default 5 | spec: 6 | clusterResourceWhitelist: 7 | - group: '*' 8 | kind: '*' 9 | destinations: 10 | - namespace: '*' 11 | server: '*' 12 | sourceNamespaces: 13 | - '*' 14 | sourceRepos: 15 | - '*' 16 | -------------------------------------------------------------------------------- /hack/dev-env/common/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - default-project.yaml 3 | - redis-secret.yaml 4 | -------------------------------------------------------------------------------- /hack/dev-env/common/redis-secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | data: 3 | auth: a3BReU5ZLWpjaGU3RUJ1Vw== 4 | kind: Secret 5 | metadata: 6 | name: argocd-redis 7 | type: Opaque 8 | -------------------------------------------------------------------------------- /hack/dev-env/control-plane/appproject-default.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: AppProject 3 | metadata: 4 | name: default 5 | spec: 6 | clusterResourceWhitelist: 7 | - group: '*' 8 | kind: '*' 9 | destinations: 10 | - namespace: '*' 11 | server: '*' 12 | sourceNamespaces: 13 | - '*' 14 | sourceRepos: 15 | - '*' 16 | -------------------------------------------------------------------------------- /hack/dev-env/control-plane/argocd-cmd-params-cm.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: argocd-cmd-params-cm 5 | data: 6 | application.namespaces: "*" 7 | -------------------------------------------------------------------------------- /hack/dev-env/control-plane/argocd-secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: argocd-secret 5 | -------------------------------------------------------------------------------- /hack/dev-env/control-plane/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - ../argo-cd/manifests/crds 3 | - ../argo-cd/manifests/base/config 4 | - ../argo-cd/manifests/base/dex 5 | - ../argo-cd/manifests/base/redis 6 | - ../argo-cd/manifests/base/repo-server 7 | - ../argo-cd/manifests/base/server 8 | - ../argo-cd/manifests/cluster-rbac/server 9 | - ../argo-cd/examples/k8s-rbac/argocd-server-applications 10 | - ../common 11 | 12 | images: 13 | - name: quay.io/argoproj/argocd 14 | newTag: LatestReleaseTag 15 | 16 | patches: 17 | - path: argocd-cmd-params-cm.yaml 18 | - path: argocd-secret.yaml 19 | - path: server-service.yaml 20 | - path: repo-server-service.yaml 21 | - path: redis-service.yaml 22 | - path: appproject-default.yaml 23 | -------------------------------------------------------------------------------- /hack/dev-env/control-plane/redis-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: argocd-redis 5 | spec: 6 | type: LoadBalancer 7 | loadBalancerIP: 192.168.56.221 8 | -------------------------------------------------------------------------------- /hack/dev-env/control-plane/repo-server-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: argocd-repo-server 5 | spec: 6 | type: LoadBalancer 7 | loadBalancerIP: 192.168.56.222 8 | -------------------------------------------------------------------------------- /hack/dev-env/control-plane/server-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: argocd-server 5 | spec: 6 | type: LoadBalancer 7 | loadBalancerIP: 192.168.56.220 8 | -------------------------------------------------------------------------------- /hack/dev-env/gen-creds.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2024 The argocd-agent Authors 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | ############################################################################## 17 | # Script to generate credentials for development/e2e-tests of argocd-agent. 18 | # 19 | # WARNING: Development script. Do not use to produce production credentials. 20 | # This script comes without any promises. It should only be used to generate 21 | # credentials for your dev or demo environments. The passwords produced are 22 | # weak. 23 | ############################################################################## 24 | 25 | set -eo pipefail 26 | if ! pwmake=$(which pwmake); then 27 | pwmake=$(which pwgen) 28 | fi 29 | 30 | SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" 31 | htpasswd=$(which htpasswd) 32 | creds_path=${SCRIPTPATH}/creds 33 | test -d ${creds_path} || mkdir ${creds_path} 34 | 35 | if test -f "${creds_path}/users.control-plane"; then 36 | echo "Truncating existing creds" 37 | rm -f "${creds_path}/users.control-plane" 38 | fi 39 | touch "${creds_path}/users.control-plane" 40 | 41 | for ag in agent-managed agent-autonomous; do 42 | password=$($pwmake 56) 43 | $htpasswd -b -B "${creds_path}/users.control-plane" "${ag}" "${password}" 44 | echo "${ag}:${password}" > "${creds_path}/creds.${ag}" 45 | done 46 | -------------------------------------------------------------------------------- /hack/dev-env/print-argo-admin-password.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -eo pipefail 4 | 5 | kubectl --context vcluster-control-plane -n argocd get secret argocd-initial-admin-secret -o jsonpath='{.data.password}' | base64 -d && echo 6 | -------------------------------------------------------------------------------- /hack/dev-env/resources/metallb-ipaddresspool.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: metallb.io/v1beta1 2 | kind: IPAddressPool 3 | metadata: 4 | name: default-addresspool 5 | namespace: metallb-system 6 | spec: 7 | addresses: 8 | - 192.168.56.100-192.168.56.254 9 | autoAssign: true 10 | -------------------------------------------------------------------------------- /hack/dev-env/resources/scc-anyuid-seccomp-netbind.yaml: -------------------------------------------------------------------------------- 1 | # This is the default any-uid OpenShift SCC, but with: 2 | # - .allowedCapabilities[0] = "NET_BIND_SERVICE" 3 | # - .seccompProfiles[0] = "runtime/default" 4 | 5 | kind: SecurityContextConstraints 6 | apiVersion: security.openshift.io/v1 7 | 8 | metadata: 9 | annotations: 10 | kubernetes.io/description: >- 11 | anyuid provides all features of the restricted SCC but allows users to run 12 | with any UID and any GID. 13 | name: anyuid-seccomp-netbind 14 | 15 | seccompProfiles: 16 | - runtime/default 17 | allowedCapabilities: 18 | - NET_BIND_SERVICE 19 | fsGroup: 20 | type: RunAsAny 21 | groups: 22 | - 'system:cluster-admins' 23 | priority: 10 24 | requiredDropCapabilities: 25 | - MKNOD 26 | runAsUser: 27 | type: RunAsAny 28 | seLinuxContext: 29 | type: MustRunAs 30 | supplementalGroups: 31 | type: RunAsAny 32 | users: [] 33 | volumes: 34 | - configMap 35 | - csi 36 | - downwardAPI 37 | - emptyDir 38 | - ephemeral 39 | - persistentVolumeClaim 40 | - projected 41 | - secret 42 | 43 | defaultAddCapabilities: null 44 | 45 | readOnlyRootFilesystem: false 46 | 47 | allowHostDirVolumePlugin: false 48 | allowHostIPC: false 49 | allowHostNetwork: false 50 | allowHostPID: false 51 | allowHostPorts: false 52 | allowPrivilegeEscalation: true 53 | allowPrivilegedContainer: false 54 | -------------------------------------------------------------------------------- /hack/dev-env/resources/vcluster.yaml: -------------------------------------------------------------------------------- 1 | controlPlane: 2 | statefulSet: 3 | security: 4 | podSecurityContext: 5 | fsGroup: 12345 6 | containerSecurityContext: 7 | runAsUser: 12345 8 | runAsNonRoot: true 9 | 10 | rbac: 11 | role: 12 | extraRules: 13 | - apiGroups: [""] 14 | resources: ["endpoints/restricted"] 15 | verbs: ["create"] -------------------------------------------------------------------------------- /hack/dev-env/reverse-tunnel/client/client.toml: -------------------------------------------------------------------------------- 1 | # client.toml 2 | [client] 3 | remote_addr = "EXTERNAL-HOSTNAME:2333" # The address of the server. The port must be the same with the port in `server.bind_addr` 4 | 5 | [client.services.one] 6 | token = "AUTHENTICATION_TOKEN" # Must be the same with the server to pass the validation 7 | local_addr = "127.0.0.1:9090" # The address of the service that needs to be forwarded 8 | 9 | # Client Side Configuration 10 | [client.transport] 11 | type = "noise" 12 | 13 | [client.transport.noise] 14 | remote_public_key = "REMOTE_PUBLIC_KEY" 15 | 16 | -------------------------------------------------------------------------------- /hack/dev-env/reverse-tunnel/client/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPTPATH="$( 4 | cd -- "$(dirname "$0")" >/dev/null 2>&1 || exit 5 | pwd -P 6 | )" 7 | 8 | docker run --network host --name rathole-proxy -it --rm -v "$SCRIPTPATH/client.toml:/app/config.toml" "quay.io/jgwest-redhat/rathole:latest@sha256:53999f80b69f9a5020e19e9c9be90fc34b973d9bd822d4fd44b968f2ebe0845f" --client /app/config.toml 9 | # Container image is built from 'rathole-image' directory 10 | 11 | # or, without docker: 12 | # 13 | # rathole --client $SCRIPTPATH/config.toml 14 | 15 | -------------------------------------------------------------------------------- /hack/dev-env/reverse-tunnel/rathole-image/artifacts/Dockerfile.new: -------------------------------------------------------------------------------- 1 | FROM rust:1.79-bookworm AS builder 2 | RUN apt update && apt install -y libssl-dev 3 | WORKDIR /home/rust/src 4 | COPY . . 5 | ARG FEATURES 6 | RUN cargo build --locked --release --features ${FEATURES:-default} 7 | RUN mkdir -p build-out/ 8 | RUN cp target/release/rathole build-out/ 9 | 10 | 11 | 12 | FROM gcr.io/distroless/cc-debian12 13 | WORKDIR /app 14 | COPY --from=builder /home/rust/src/build-out/rathole . 15 | USER 1000:1000 16 | ENTRYPOINT ["./rathole"] 17 | -------------------------------------------------------------------------------- /hack/dev-env/reverse-tunnel/rathole-image/build-and-push.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Build and push a multi-arch docker container containing latest rathole release built from source. 4 | 5 | # I needed to run this first: 6 | # - docker buildx create --name mybuilder --driver docker-container --use 7 | # I'm on Linux x64 (Fedora) 8 | 9 | REPO=quay.io/jgwest-redhat 10 | 11 | set -ex 12 | 13 | SCRIPTPATH="$( 14 | cd -- "$(dirname "$0")" >/dev/null 2>&1 || exit 15 | pwd -P 16 | )" 17 | 18 | TEMP_DIR=$(mktemp -d) 19 | 20 | cd $TEMP_DIR 21 | git clone https://github.com/yujqiao/rathole 22 | 23 | cd rathole 24 | 25 | git checkout ebb764ae53d7ffe4fcb45f83f7563bec5c74199d # v0.5.0 26 | 27 | cp $SCRIPTPATH/artifacts/Dockerfile.new $TEMP_DIR/rathole/Dockerfile 28 | 29 | docker buildx build -t $REPO/rathole:latest --platform linux/amd64,linux/arm64 --push . 30 | 31 | -------------------------------------------------------------------------------- /hack/dev-env/reverse-tunnel/server/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: rathole-container 5 | spec: 6 | replicas: 1 7 | revisionHistoryLimit: 3 8 | selector: 9 | matchLabels: 10 | app: rathole-container 11 | template: 12 | metadata: 13 | labels: 14 | app: rathole-container 15 | spec: 16 | containers: 17 | - image: 'quay.io/jgwest-redhat/rathole:latest@sha256:53999f80b69f9a5020e19e9c9be90fc34b973d9bd822d4fd44b968f2ebe0845f' # built by rathole-image directory 18 | name: rathole-container 19 | ports: 20 | - containerPort: 2333 21 | protocol: TCP 22 | - containerPort: 9090 23 | protocol: TCP 24 | command: 25 | - /app/rathole 26 | args: 27 | - '--server' 28 | - /config/server.toml 29 | volumeMounts: 30 | - name: config-volume 31 | mountPath: /config 32 | volumes: 33 | - name: config-volume 34 | secret: 35 | secretName: rathole-container 36 | -------------------------------------------------------------------------------- /hack/dev-env/reverse-tunnel/server/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - deployment.yaml 6 | - service.yaml 7 | - service-internal.yaml 8 | 9 | secretGenerator: 10 | - name: rathole-container 11 | files: 12 | - server.toml 13 | -------------------------------------------------------------------------------- /hack/dev-env/reverse-tunnel/server/server.toml: -------------------------------------------------------------------------------- 1 | # server.toml 2 | [server] 3 | bind_addr = "0.0.0.0:2333" # `2333` specifies the port that rathole listens for clients 4 | 5 | [server.services.one] 6 | token = "AUTHENTICATION_TOKEN" # Token that is used to authenticate the client for the service. Change to a arbitrary value. 7 | bind_addr = "0.0.0.0:9090" # `9090` specifies the port that exposes `one` to the Internet 8 | 9 | # Server Side Configuration 10 | [server.transport] 11 | type = "noise" 12 | 13 | [server.transport.noise] 14 | local_private_key = "LOCAL_PRIVATE_KEY" 15 | -------------------------------------------------------------------------------- /hack/dev-env/reverse-tunnel/server/service-internal.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: rathole-container-internal 5 | spec: 6 | ports: 7 | - name: "9090" 8 | port: 9090 9 | targetPort: 9090 10 | protocol: TCP 11 | selector: 12 | app: rathole-container -------------------------------------------------------------------------------- /hack/dev-env/reverse-tunnel/server/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: rathole-container-external 5 | spec: 6 | selector: 7 | app: rathole-container 8 | # type: NodePort 9 | type: LoadBalancer 10 | ports: 11 | - protocol: TCP 12 | name: "2333" 13 | port: 2333 # The port the service listens on inside the cluster 14 | targetPort: 2333 # The port your application is listening on in the pod 15 | # nodePort: 30000 # The static port exposed on each node (optional, Kubernetes assigns if omitted) 16 | -------------------------------------------------------------------------------- /hack/dev-env/start-agent-autonomous.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2024 The argocd-agent Authors 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | set -ex -o pipefail 17 | ARGS=$* 18 | if ! kubectl config get-contexts | tail -n +2 | awk '{ print $2 }' | grep -qE '^vcluster-agent-autonomous$'; then 19 | echo "kube context vcluster-agent-autonomous is not configured; missing setup?" >&2 20 | exit 1 21 | fi 22 | SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" 23 | 24 | echo $ARGOCD_AGENT_REMOTE_PORT 25 | export ARGOCD_AGENT_REMOTE_PORT=${ARGOCD_AGENT_REMOTE_PORT:-8443} 26 | 27 | # Point the agent to the toxiproxy server if it is configured from the e2e tests 28 | E2E_ENV_FILE="/tmp/argocd-agent-e2e" 29 | if [ -f "$E2E_ENV_FILE" ]; then 30 | source "$E2E_ENV_FILE" 31 | fi 32 | 33 | go run github.com/argoproj-labs/argocd-agent/cmd/agent \ 34 | --agent-mode autonomous \ 35 | --creds mtls:any \ 36 | --server-address 127.0.0.1 \ 37 | --insecure-tls \ 38 | --kubecontext vcluster-agent-autonomous \ 39 | --namespace argocd \ 40 | --log-level trace $ARGS \ 41 | --metrics-port 8182 \ 42 | --healthz-port 8002 \ 43 | #--enable-compression true 44 | #--keep-alive-ping-interval 15m 45 | -------------------------------------------------------------------------------- /hack/dev-env/start-agent-managed.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2024 The argocd-agent Authors 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | set -ex -o pipefail 17 | ARGS=$* 18 | if ! kubectl config get-contexts | tail -n +2 | awk '{ print $2 }' | grep -qE '^vcluster-agent-managed$'; then 19 | echo "kube context vcluster-agent-managed is not configured; missing setup?" >&2 20 | exit 1 21 | fi 22 | SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" 23 | 24 | export ARGOCD_AGENT_REMOTE_PORT=${ARGOCD_AGENT_REMOTE_PORT:-8443} 25 | 26 | # Point the agent to the toxiproxy server if it is configured from the e2e tests 27 | E2E_ENV_FILE="/tmp/argocd-agent-e2e" 28 | if [ -f "$E2E_ENV_FILE" ]; then 29 | source "$E2E_ENV_FILE" 30 | fi 31 | 32 | go run github.com/argoproj-labs/argocd-agent/cmd/agent \ 33 | --agent-mode managed \ 34 | --creds "mtls:any" \ 35 | --server-address 127.0.0.1 \ 36 | --insecure-tls \ 37 | --kubecontext vcluster-agent-managed \ 38 | --namespace agent-managed \ 39 | --log-level trace $ARGS \ 40 | --healthz-port 8001 \ 41 | #--enable-compression true 42 | #--keep-alive-ping-interval 15m 43 | -------------------------------------------------------------------------------- /hack/dev-env/start-principal.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2024 The argocd-agent Authors 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | set -ex -o pipefail 17 | ARGS=$* 18 | if ! kubectl config get-contexts | tail -n +2 | awk '{ print $2 }' | grep -qE '^vcluster-control-plane$'; then 19 | echo "kube context vcluster-control-plane is not configured; missing setup?" >&2 20 | exit 1 21 | fi 22 | 23 | if test "${ARGOCD_PRINCIPAL_REDIS_SERVER_ADDRESS}" = ""; then 24 | ipaddr=$(kubectl --context vcluster-control-plane -n argocd get svc argocd-redis -o jsonpath='{.status.loadBalancer.ingress[0].ip}') 25 | hostname=$(kubectl --context vcluster-control-plane -n argocd get svc argocd-redis -o jsonpath='{.status.loadBalancer.ingress[0].hostname}') 26 | if test "$ipaddr" != ""; then 27 | ARGOCD_PRINCIPAL_REDIS_SERVER_ADDRESS=$ipaddr:6379 28 | elif test "$hostname" != ""; then 29 | ARGOCD_PRINCIPAL_REDIS_SERVER_ADDRESS=$hostname:6379 30 | else 31 | echo "Could not determine Redis server address." >&2 32 | echo "Please set ARGOCD_PRINCIPAL_REDIS_SERVER_ADDRESS manually" >&2 33 | exit 1 34 | fi 35 | export ARGOCD_PRINCIPAL_REDIS_SERVER_ADDRESS 36 | fi 37 | 38 | SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" 39 | go run github.com/argoproj-labs/argocd-agent/cmd/principal \ 40 | --allowed-namespaces '*' \ 41 | --insecure-jwt-generate \ 42 | --kubecontext vcluster-control-plane \ 43 | --log-level trace \ 44 | --namespace argocd \ 45 | --auth "mtls:CN=([^,]+)" \ 46 | $ARGS 47 | #--auth "userpass:${SCRIPTPATH}/creds/users.control-plane" $ARGS 48 | -------------------------------------------------------------------------------- /hack/generate-proto.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2024 The argocd-agent Authors 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | 17 | set -eo pipefail 18 | 19 | PROJECT_ROOT=$(cd "$(dirname "${BASH_SOURCE}")"/..; pwd) 20 | PATH=${PROJECT_ROOT}/build/bin:${PATH} 21 | 22 | GENERATE_PATHS=" 23 | ${PROJECT_ROOT}/principal/apis/auth;authapi 24 | ${PROJECT_ROOT}/principal/apis/eventstream;eventstreamapi 25 | ${PROJECT_ROOT}/principal/apis/version;versionapi 26 | " 27 | 28 | for p in ${GENERATE_PATHS}; do 29 | set -x 30 | IFS=";" 31 | set -- $p 32 | src_path=$1 33 | api_name=$2 34 | unset IFS 35 | files= 36 | for f in $(ls $src_path/*.proto); do 37 | echo "--> Generating Protobuf and gRPC client for $api_name" 38 | mkdir -p ${PROJECT_ROOT}/pkg/api/grpc/${api_name} 39 | ${PROJECT_ROOT}/build/bin/protoc -I=${src_path} \ 40 | -I=${PROJECT_ROOT}/vendor \ 41 | -I=${PROJECT_ROOT}/proto \ 42 | -I=${PROJECT_ROOT}/build/bin/protoc-include \ 43 | --go_out=${PROJECT_ROOT}/pkg/api/grpc/${api_name} \ 44 | --go_opt=paths=source_relative \ 45 | --go-grpc_out=${PROJECT_ROOT}/pkg/api/grpc/${api_name} \ 46 | --go-grpc_opt=paths=source_relative \ 47 | $f 48 | done 49 | done 50 | -------------------------------------------------------------------------------- /hack/install/install-codegen-go-tools.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2024 The argocd-agent Authors 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | 17 | PROJECT_ROOT=$(cd $(dirname ${BASH_SOURCE})/../..; pwd) 18 | DIST_PATH="${PROJECT_ROOT}/build/bin" 19 | mkdir -p "${DIST_PATH}" 20 | export GOBIN="${DIST_PATH}" 21 | 22 | go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28 23 | go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2 24 | -------------------------------------------------------------------------------- /hack/install/install-protoc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2024 The argocd-agent Authors 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | set -eux -o pipefail 17 | 18 | PROJECT_ROOT=$(cd $(dirname ${BASH_SOURCE})/../..; pwd) 19 | DIST_PATH="${PROJECT_ROOT}/build/bin" 20 | mkdir -p "${DIST_PATH}" 21 | PATH="${DIST_PATH}:${PATH}" 22 | 23 | protoc_version="25.3" 24 | OS=$(go env GOOS) 25 | ARCHITECTURE=$(go env GOARCH) 26 | DOWNLOADS=$(mktemp -d /tmp/downloads.XXXXXXXXX) 27 | mkdir -p ${DIST_PATH} 28 | case $OS in 29 | darwin) 30 | # For macOS, the x86_64 binary is used even on Apple Silicon (it is run through rosetta), so 31 | # we download and install the x86_64 version. See: https://github.com/protocolbuffers/protobuf/pull/8557 32 | protoc_os=osx 33 | protoc_arch=x86_64 34 | ;; 35 | *) 36 | protoc_os=linux 37 | case $ARCHITECTURE in 38 | arm64|arm) 39 | protoc_arch=aarch_64 40 | ;; 41 | s390x) 42 | protoc_arch=s390_64 43 | ;; 44 | ppc64le) 45 | protoc_arch=ppcle_64 46 | ;; 47 | *) 48 | protoc_arch=x86_64 49 | ;; 50 | esac 51 | ;; 52 | esac 53 | 54 | export TARGET_FILE=protoc_${protoc_version}_${OS}_${ARCHITECTURE}.zip 55 | url=https://github.com/protocolbuffers/protobuf/releases/download/v${protoc_version}/protoc-${protoc_version}-${protoc_os}-${protoc_arch}.zip 56 | [ -e $DOWNLOADS/${TARGET_FILE} ] || curl -sLf --retry 3 -o $DOWNLOADS/${TARGET_FILE} ${url} 57 | #$(dirname $0)/compare-chksum.sh 58 | mkdir -p /tmp/protoc-${protoc_version} 59 | unzip -o $DOWNLOADS/${TARGET_FILE} -d /tmp/protoc-${protoc_version} 60 | mkdir -p ${DIST_PATH}/protoc-include 61 | install -m 0755 /tmp/protoc-${protoc_version}/bin/protoc ${DIST_PATH}/protoc 62 | cp -a /tmp/protoc-${protoc_version}/include/* ${DIST_PATH}/protoc-include 63 | #chmod -R ${DIST_PATH}/protoc-include 64 | protoc --version 65 | -------------------------------------------------------------------------------- /hack/profile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright 2024 The argocd-agent Authors 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | 17 | set -eo pipefail 18 | 19 | go_packages=$(go list ./...) 20 | for pkg in $go_packages; do 21 | path=$(echo $pkg | sed -e 's,github.com/argoproj-labs/argocd-application-agent/,,') 22 | mkdir -p test/profile/$path 23 | go test -race -mutexprofile test/profile/$path/mutex.profile $pkg || true 24 | done 25 | -------------------------------------------------------------------------------- /hack/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2024 The argocd-agent Authors 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # Wrapper script to run unit tests for argocd-agent 17 | set -eo pipefail 18 | UNIT_LIST=$(go list ./... | grep -E -v '(mock|mocks|e2e|fake|pkg/api/grpc)') 19 | go test -race -timeout 10m -coverprofile test/out/coverage.out $UNIT_LIST 20 | -------------------------------------------------------------------------------- /hack/users.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright 2024 The argocd-agent Authors 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # requires httpd-tools (htpasswd) installed 17 | set -e -o pipefail 18 | action="$1" 19 | username="$2" 20 | password="$3" 21 | 22 | passwdfile="argocd-agent.passwd" 23 | 24 | add_user() { 25 | local username="$1" 26 | local password="$2" 27 | if test -z "$password"; then 28 | htpasswd -BC 10 "$passwdfile" "$username" 29 | else 30 | htpasswd -BbC 10 "$passwdfile" "$username" "${password}" 31 | fi 32 | } 33 | 34 | rm_user() { 35 | local username="$1" 36 | htpasswd -D "$passwdfile" "$username" 37 | } 38 | 39 | usage() { 40 | echo "USAGE: $0 add []" >&2 41 | echo " $0 rm " >&2 42 | } 43 | 44 | test -f $passwdfile || touch $passwdfile 45 | 46 | case "$action" in 47 | "add") 48 | test -z "$username" && { 49 | usage 50 | exit 1 51 | } 52 | add_user "$username" "$password" 53 | ;; 54 | "rm") 55 | test -z "$username" && { 56 | usage 57 | exit 1 58 | } 59 | rm_user "$username" 60 | ;; 61 | *) 62 | usage 63 | exit 1 64 | ;; 65 | esac 66 | 67 | -------------------------------------------------------------------------------- /hack/vscode/launch.json: -------------------------------------------------------------------------------- 1 | // vscode launch configuration for debugging argocd-agent 2 | { 3 | "version": "0.2.0", 4 | "configurations": [ 5 | { 6 | "name": "Debug principal", 7 | "type": "go", 8 | "request": "launch", 9 | "mode": "debug", 10 | "program": "${workspaceFolder}/cmd/principal", 11 | "args": [ 12 | "--allowed-namespaces='*'", 13 | "--insecure-tls-generate", 14 | "--insecure-jwt-generate", 15 | "--kubecontext=vcluster-control-plane", 16 | "--log-level=trace", 17 | "--auth=userpass:${workspaceFolder}/hack/demo-env/creds/users.control-plane" 18 | ] 19 | }, 20 | { 21 | "name": "Debug agent-autonomous", 22 | "type": "go", 23 | "request": "launch", 24 | "mode": "debug", 25 | "program": "${workspaceFolder}/cmd/agent", 26 | "args": [ 27 | "--agent-mode=autonomous", 28 | "--creds=userpass:${workspaceFolder}/hack/demo-env/creds/creds.agent-autonomous", 29 | "--server-address=127.0.0.1", 30 | "--server-port=8443", 31 | "--insecure-tls", 32 | "--kubecontext=vcluster-agent-autonomous", 33 | "--log-level=trace", 34 | "--namespace=argocd" 35 | ] 36 | }, 37 | { 38 | "name": "Debug agent-managed", 39 | "type": "go", 40 | "request": "launch", 41 | "mode": "debug", 42 | "program": "${workspaceFolder}/cmd/agent", 43 | "args": [ 44 | "--agent-mode=managed", 45 | "--creds=userpass:${workspaceFolder}/hack/demo-env/creds/creds.agent-autonomous", 46 | "--server-address=127.0.0.1", 47 | "--server-port=8443", 48 | "--insecure-tls", 49 | "--kubecontext=vcluster-agent-autonomous", 50 | "--log-level=trace", 51 | "--namespace=argocd" 52 | ] 53 | } 54 | ] 55 | } -------------------------------------------------------------------------------- /install/kubernetes/agent/agent-metrics-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: argocd-agent-agent 6 | app.kubernetes.io/part-of: argocd-agent 7 | app.kubernetes.io/component: agent 8 | name: argocd-agent-agent-metrics 9 | spec: 10 | ports: 11 | - name: metrics 12 | protocol: TCP 13 | port: 8181 14 | targetPort: 8181 15 | selector: 16 | app.kubernetes.io/name: argocd-agent-agent 17 | -------------------------------------------------------------------------------- /install/kubernetes/agent/agent-params-cm.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: argocd-agent-params 5 | data: 6 | # agent.mode: The mode this agent should operate in. Valid values are 7 | # "autonomous" or "managed". 8 | # Default: "autonomous" 9 | agent.mode: "autonomous" 10 | # agent.creds: Valid credential identifier for this agent. Must be in the 11 | # format :. Valid values are: 12 | # - "userpass:_path_to_encrypted_creds_" where _path_to_encrypted_creds_ is 13 | # the path to the file containing encrypted credential for authenticatiion. 14 | # - "mtls:_agent_id_regex_" where _agent_id_regex_ is the regex pattern for 15 | # extracting the agent ID from client cert subject. 16 | # Default: "" 17 | agent.creds: "userpass:/app/config/creds/userpass.creds" 18 | # agent.tls.client.insecure: Whether to skip the validation of the remote TLS 19 | # credentials. Insecure. Do only use for development purposes. 20 | # Default: false 21 | agent.tls.client.insecure: "false" 22 | # agent.tls.root-ca-path: The path to a file containing the certificates for 23 | # the TLS root certificate authority used to validate the remote principal. 24 | # Default: "" 25 | agent.tls.root-ca-path: "/app/config/tls/ca.crt" 26 | # agent.tls.client.cert-path: Path to a file containing the agent's TLS client 27 | # certificate. 28 | # Default: "" 29 | agent.tls.client.cert-path: "/app/config/tls/tls.crt" 30 | # agent.tls.client.cert-path: Path to a file containing the agent's TLS client 31 | # private key. 32 | # Default: "" 33 | agent.tls.client.key-path: "/app/config/tls/tls.key" 34 | # agent.log.level: The log level the agent should use. Valid values are 35 | # trace, debug, info, warn and error. 36 | # Default: "info" 37 | agent.log.level: "info" 38 | # agent.namespace: The namespace the agent should operate and manage the 39 | # Argo CD resources in. 40 | # Default: "argocd" 41 | agent.namespace: "argocd" 42 | # agent.principal.address: The remote address of the principal to connect 43 | # to. Can be a DNS name, an IPv4 address or an IPv6 address. 44 | # Default: "" 45 | agent.server.address: "argocd-agent-principal.example.com" 46 | # agent.server.port: The remote port of the principal to connect to. 47 | # Default: "443" 48 | agent.server.port: "443" 49 | # agent.metrics.port: The port the metrics server should listen on. 50 | # Default: 8181 51 | agent.metrics.port: "8181" -------------------------------------------------------------------------------- /install/kubernetes/agent/agent-role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: Role 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: argocd-agent-agent 6 | app.kubernetes.io/part-of: argocd-agent 7 | app.kubernetes.io/component: agent 8 | name: argocd-agent-agent 9 | rules: 10 | - apiGroups: 11 | - argoproj.io 12 | resources: 13 | - applications 14 | - appprojects 15 | - applicationsets 16 | verbs: 17 | - create 18 | - get 19 | - list 20 | - watch 21 | - update 22 | - delete 23 | - patch 24 | - apiGroups: 25 | - "" 26 | resources: 27 | - secrets 28 | - configmaps 29 | verbs: 30 | - create 31 | - get 32 | - list 33 | - watch 34 | - update 35 | - patch 36 | - delete 37 | - apiGroups: 38 | - "" 39 | resources: 40 | - events 41 | verbs: 42 | - create 43 | - list -------------------------------------------------------------------------------- /install/kubernetes/agent/agent-rolebinding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: argocd-agent-agent 6 | app.kubernetes.io/part-of: argocd-agent 7 | app.kubernetes.io/component: agent 8 | name: argocd-agent-agent 9 | roleRef: 10 | apiGroup: rbac.authorization.k8s.io 11 | kind: Role 12 | name: argocd-agent-agent 13 | subjects: 14 | - kind: ServiceAccount 15 | name: argocd-agent-agent 16 | -------------------------------------------------------------------------------- /install/kubernetes/agent/agent-sa.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: argocd-agent-agent 6 | app.kubernetes.io/part-of: argocd-agent 7 | app.kubernetes.io/component: agent 8 | name: argocd-agent-agent 9 | -------------------------------------------------------------------------------- /install/kubernetes/agent/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - agent-sa.yaml 3 | - agent-role.yaml 4 | - agent-rolebinding.yaml 5 | - agent-deployment.yaml 6 | - agent-params-cm.yaml 7 | - agent-metrics-service.yaml 8 | -------------------------------------------------------------------------------- /install/kubernetes/base/deployment/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - deployment.yaml -------------------------------------------------------------------------------- /install/kubernetes/principal/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - principal-sa.yaml 3 | - principal-role.yaml 4 | - principal-clusterrole.yaml 5 | - principal-rolebinding.yaml 6 | - principal-clusterrolebinding.yaml 7 | - principal-deployment.yaml 8 | - principal-service.yaml 9 | - principal-params-cm.yaml 10 | - principal-metrics-service.yaml 11 | -------------------------------------------------------------------------------- /install/kubernetes/principal/principal-clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: argocd-agent-principal 6 | app.kubernetes.io/part-of: argocd-agent 7 | app.kubernetes.io/component: principal 8 | name: argocd-agent-principal 9 | rules: 10 | - apiGroups: 11 | - argoproj.io 12 | resources: 13 | - applications 14 | - appprojects 15 | - applicationsets 16 | verbs: 17 | - create 18 | - get 19 | - list 20 | - watch 21 | - update 22 | - delete 23 | - patch 24 | -------------------------------------------------------------------------------- /install/kubernetes/principal/principal-clusterrolebinding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: argocd-agent-principal 6 | app.kubernetes.io/part-of: argocd-agent 7 | app.kubernetes.io/component: principal 8 | name: argocd-agent-principal 9 | roleRef: 10 | apiGroup: rbac.authorization.k8s.io 11 | kind: ClusterRole 12 | name: argocd-agent-principal 13 | subjects: 14 | - kind: ServiceAccount 15 | name: argocd-agent-principal 16 | namespace: argocd 17 | -------------------------------------------------------------------------------- /install/kubernetes/principal/principal-metrics-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: argocd-agent-principal 6 | app.kubernetes.io/part-of: argocd-agent 7 | app.kubernetes.io/component: principal 8 | name: argocd-agent-principal-metrics 9 | spec: 10 | ports: 11 | - name: metrics 12 | protocol: TCP 13 | port: 8000 14 | targetPort: 8000 15 | selector: 16 | app.kubernetes.io/name: argocd-agent-principal 17 | -------------------------------------------------------------------------------- /install/kubernetes/principal/principal-role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: Role 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: argocd-agent-principal 6 | app.kubernetes.io/part-of: argocd-agent 7 | app.kubernetes.io/component: principal 8 | name: argocd-agent-principal 9 | rules: 10 | - apiGroups: 11 | - "" 12 | resources: 13 | - secrets 14 | - configmaps 15 | verbs: 16 | - create 17 | - get 18 | - list 19 | - watch 20 | - update 21 | - patch 22 | - delete 23 | - apiGroups: 24 | - "" 25 | resources: 26 | - events 27 | verbs: 28 | - create 29 | - list -------------------------------------------------------------------------------- /install/kubernetes/principal/principal-rolebinding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: argocd-agent-principal 6 | app.kubernetes.io/part-of: argocd-agent 7 | app.kubernetes.io/component: principal 8 | name: argocd-agent-principal 9 | roleRef: 10 | apiGroup: rbac.authorization.k8s.io 11 | kind: Role 12 | name: argocd-agent-principal 13 | subjects: 14 | - kind: ServiceAccount 15 | name: argocd-agent-principal 16 | -------------------------------------------------------------------------------- /install/kubernetes/principal/principal-sa.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: argocd-agent-principal 6 | app.kubernetes.io/part-of: argocd-agent 7 | app.kubernetes.io/component: principal 8 | name: argocd-agent-principal 9 | -------------------------------------------------------------------------------- /install/kubernetes/principal/principal-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: argocd-agent-principal 6 | app.kubernetes.io/part-of: argocd-agent 7 | app.kubernetes.io/component: principal 8 | name: argocd-agent-principal 9 | spec: 10 | ports: 11 | - name: https 12 | protocol: TCP 13 | port: 443 14 | targetPort: 8443 15 | selector: 16 | app.kubernetes.io/name: argocd-agent-principal 17 | -------------------------------------------------------------------------------- /install/kubernetes/principal/principal-tls-secret.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj-labs/argocd-agent/ea8f330e68feb5c72d7c2ca0e3426f22e83a7dd6/install/kubernetes/principal/principal-tls-secret.yaml -------------------------------------------------------------------------------- /install/kubernetes/principal/principal-userpass-secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: argocd-agent-principal-userpass 5 | stringData: 6 | passwd: "" -------------------------------------------------------------------------------- /internal/argocd/cluster/conversion.go: -------------------------------------------------------------------------------- 1 | package cluster 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "strconv" 7 | "strings" 8 | "time" 9 | 10 | "github.com/argoproj/argo-cd/v2/common" 11 | "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" 12 | v1 "k8s.io/api/core/v1" 13 | ) 14 | 15 | /* 16 | The ClusterToSecret function has been copied from Argo CD util/db/cluster.go 17 | and was slightly modified. It is a package-private function in Argo CD, so 18 | we are not able to just use it from the package. 19 | 20 | TODO(jannfis): Submit PR to Argo CD to make this function public, so we can 21 | just use it directly from github.com/argoproj/argo-cd/v2 package. 22 | */ 23 | 24 | // ClusterToSecret converts a cluster object to string data for serialization to a secret 25 | func ClusterToSecret(c *v1alpha1.Cluster, secret *v1.Secret) error { 26 | data := make(map[string][]byte) 27 | data["server"] = []byte(strings.TrimRight(c.Server, "/")) 28 | if c.Name == "" { 29 | data["name"] = []byte(c.Server) 30 | } else { 31 | data["name"] = []byte(c.Name) 32 | } 33 | if len(c.Namespaces) != 0 { 34 | data["namespaces"] = []byte(strings.Join(c.Namespaces, ",")) 35 | } 36 | configBytes, err := json.Marshal(c.Config) 37 | if err != nil { 38 | return err 39 | } 40 | data["config"] = configBytes 41 | if c.Shard != nil { 42 | data["shard"] = []byte(strconv.Itoa(int(*c.Shard))) 43 | } 44 | if c.ClusterResources { 45 | data["clusterResources"] = []byte("true") 46 | } 47 | if c.Project != "" { 48 | data["project"] = []byte(c.Project) 49 | } 50 | secret.Data = data 51 | 52 | secret.Labels = c.Labels 53 | if c.Annotations != nil && c.Annotations[v1.LastAppliedConfigAnnotation] != "" { 54 | return fmt.Errorf("annotation %s cannot be set", v1.LastAppliedConfigAnnotation) 55 | } 56 | secret.Annotations = c.Annotations 57 | 58 | if secret.Annotations == nil { 59 | secret.Annotations = make(map[string]string) 60 | } 61 | 62 | if c.RefreshRequestedAt != nil { 63 | secret.Annotations[v1alpha1.AnnotationKeyRefresh] = c.RefreshRequestedAt.Format(time.RFC3339) 64 | } else { 65 | delete(secret.Annotations, v1alpha1.AnnotationKeyRefresh) 66 | } 67 | 68 | if secret.Annotations == nil { 69 | secret.Annotations = map[string]string{} 70 | } 71 | secret.Annotations[common.AnnotationKeyManagedBy] = LabelValueManagerName 72 | 73 | if secret.Labels == nil { 74 | secret.Labels = map[string]string{} 75 | } 76 | secret.Labels[common.LabelKeySecretType] = common.LabelValueSecretTypeCluster 77 | 78 | return nil 79 | } 80 | -------------------------------------------------------------------------------- /internal/argocd/cluster/mapping_test.go: -------------------------------------------------------------------------------- 1 | package cluster 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | 8 | "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" 9 | ) 10 | 11 | func Test_ClusterMappings(t *testing.T) { 12 | m := &Manager{ 13 | clusters: make(map[string]*v1alpha1.Cluster), 14 | } 15 | t.Run("Map cluster to agent", func(t *testing.T) { 16 | err := m.MapCluster("agent", &v1alpha1.Cluster{Name: "cluster"}) 17 | require.NoError(t, err) 18 | }) 19 | t.Run("Cluster is mapped successfully", func(t *testing.T) { 20 | require.True(t, m.HasMapping("agent")) 21 | require.Equal(t, "cluster", m.Mapping("agent").Name) 22 | }) 23 | t.Run("Agent cannot be mapped again", func(t *testing.T) { 24 | err := m.MapCluster("agent", &v1alpha1.Cluster{}) 25 | require.ErrorIs(t, err, ErrAlreadyMapped) 26 | }) 27 | t.Run("Mapping can be deleted", func(t *testing.T) { 28 | err := m.UnmapCluster("agent") 29 | require.NoError(t, err) 30 | }) 31 | t.Run("Mapping has been deleted", func(t *testing.T) { 32 | require.False(t, m.HasMapping("agent")) 33 | require.Nil(t, m.Mapping("agent")) 34 | }) 35 | t.Run("Unmap on unmapped agent returns error", func(t *testing.T) { 36 | require.ErrorIs(t, m.UnmapCluster("agent"), ErrNotMapped) 37 | }) 38 | } 39 | -------------------------------------------------------------------------------- /internal/auth/interface.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package auth 16 | 17 | import "context" 18 | 19 | type AuthSubject struct { 20 | ClientID string `json:"clientID"` 21 | Mode string `json:"mode"` 22 | } 23 | 24 | // Credentials is a data type for passing arbitrary credentials to auth methods 25 | type Credentials map[string]string 26 | 27 | // Method is the interface to be implemented by all auth methods 28 | type Method interface { 29 | Init() error 30 | Authenticate(ctx context.Context, credentials Credentials) (string, error) 31 | } 32 | -------------------------------------------------------------------------------- /internal/auth/methods.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package auth 16 | 17 | import ( 18 | "fmt" 19 | "sync" 20 | ) 21 | 22 | // Methods provide a thread-safe way to register and look up available auth 23 | // methods. 24 | type Methods struct { 25 | lock sync.RWMutex 26 | methods map[string]Method 27 | } 28 | 29 | func NewMethods() *Methods { 30 | return &Methods{methods: make(map[string]Method)} 31 | } 32 | 33 | // RegisterMethod registers the auth method with the given name. If another 34 | // method is already registered with the same name, returns error. 35 | func (m *Methods) RegisterMethod(name string, method Method) error { 36 | m.lock.Lock() 37 | defer m.lock.Unlock() 38 | _, ok := m.methods[name] 39 | if ok { 40 | return fmt.Errorf("auth method %s already registered", name) 41 | } 42 | m.methods[name] = method 43 | return nil 44 | } 45 | 46 | func (m *Methods) Names() []string { 47 | m.lock.RLock() 48 | defer m.lock.RUnlock() 49 | ret := []string{} 50 | for n := range m.methods { 51 | ret = append(ret, n) 52 | } 53 | return ret 54 | } 55 | 56 | // Method gets the authentication method identified by name. If no such 57 | // method exists, returns nil. 58 | func (m *Methods) Method(name string) Method { 59 | m.lock.RLock() 60 | defer m.lock.RUnlock() 61 | method, ok := m.methods[name] 62 | if !ok { 63 | return nil 64 | } 65 | return method 66 | } 67 | -------------------------------------------------------------------------------- /internal/auth/methods_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package auth 16 | 17 | import ( 18 | "context" 19 | "testing" 20 | 21 | "github.com/stretchr/testify/require" 22 | ) 23 | 24 | type mockAuth struct{} 25 | 26 | func (a *mockAuth) Authenticate(ctx context.Context, creds Credentials) (string, error) { 27 | return "some", nil 28 | } 29 | 30 | func (a *mockAuth) Init() error { 31 | return nil 32 | } 33 | 34 | func Test_AuthMethods(t *testing.T) { 35 | t.Run("Register an auth method and verify", func(t *testing.T) { 36 | m := NewMethods() 37 | authmethod := &mockAuth{} 38 | err := m.RegisterMethod("userpass", authmethod) 39 | require.NoError(t, err) 40 | require.NotNil(t, m.Method("userpass")) 41 | }) 42 | 43 | t.Run("Register two auth methods under same name", func(t *testing.T) { 44 | m := NewMethods() 45 | authmethod := &mockAuth{} 46 | err := m.RegisterMethod("userpass", authmethod) 47 | require.NoError(t, err) 48 | err = m.RegisterMethod("userpass", authmethod) 49 | require.Error(t, err) 50 | }) 51 | 52 | t.Run("Look up non-existing auth method", func(t *testing.T) { 53 | m := NewMethods() 54 | authmethod := &mockAuth{} 55 | err := m.RegisterMethod("userpass", authmethod) 56 | require.NoError(t, err) 57 | require.NotNil(t, m.Method("userpass")) 58 | require.Nil(t, m.Method("username")) 59 | }) 60 | 61 | } 62 | -------------------------------------------------------------------------------- /internal/auth/userpass/testdata/generate-testdata.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright 2024 The argocd-agent Authors 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # requires openssl and httpd-tools (htpasswd) installed 17 | for n in 1 2 3 4 5; do 18 | client_id=$(openssl rand -hex 16) 19 | client_secret=$(openssl rand -hex 32) 20 | hash=$(htpasswd -bnBC 10 "" $client_secret | tr -d ':\n') 21 | echo "# $client_secret" 22 | echo $client_id:$hash 23 | done 24 | -------------------------------------------------------------------------------- /internal/auth/userpass/testdata/userdb-good.txt: -------------------------------------------------------------------------------- 1 | # 9445bdf65eeeebb045b55f70985ba81527bbd5f2fb3a4486bd8ea57af9654ac9 2 | 8ccafd2c3a368568a8da3706ea2b4a0a:$2y$10$up1JiECgkjdrftdbq/kMvOY9keQt9fgmAPJnhIt1HY3DumikOiw3y 3 | # c1eb8b2a13fa6e9df5bb420cd6ee56e67d14d7b2adca0d6eccfa9509e0a1e7e4 4 | 250c284e71edd8c4e9ac243442209b9e:$2y$10$7U92nTHCVyE16BMnN5zOoeLE6QS7Z.mMRT6s5VnFekIxhN8c/ZMaW 5 | # f1625a213ae6c6922634450e795248cee81518160562cdc453dc74d5e0d445b8 6 | 18828124e1c604755e362799e4311080:$2y$10$lVK6ZY5sAHTItX3QEn392uXl/37xc0jCIqUemEAsL8S4YRFOQLCP. 7 | # b7d23783e2c447c64204a6b33a40cb06ddab9f02ee16a484e93977aa21a9e7aa 8 | 2aa88e85203408c684ca95dc1f391c99:$2y$10$JRF2aO3ErJ9CTS.4b2Dnu.ychL67c.dQ33UopvujYjhGtibmeV0Ay 9 | # 426055d6d0d6b79b38faa64b76dcdaec02b9e5607444a4a5e7b60e30e148d5f5 10 | ee92794742387a7bd41e6b9d8765c9e7:$2y$10$Sg8rj10osHE3CI8BykCkBOsVPboK/16teppV6q3rabQmos2iwGrvK 11 | -------------------------------------------------------------------------------- /internal/auth/userpass/testdata/userdb-partial.txt: -------------------------------------------------------------------------------- 1 | # 9445bdf65eeeebb045b55f70985ba81527bbd5f2fb3a4486bd8ea57af9654ac9 2 | 8ccafd2c3a368568a8da3706ea2b4a0a:$2y$10$up1JiECgkjdrftdbq/kMvOY9keQt9fgmAPJnhIt1HY3DumikOiw3y 3 | # c1eb8b2a13fa6e9df5bb420cd6ee56e67d14d7b2adca0d6eccfa9509e0a1e7e4 4 | e:7U92nTHCVyE16BMnN5zOoeLE6QS7Z.mMRT6s5VnFekIxhN8c/ZMaW 5 | # f1625a213ae6c6922634450e795248cee81518160562cdc453dc74d5e0d445b8 6 | :$2y$10$lVK6ZY5sAHTItX3QEn392uXl/37xc0jCIqUemEAsL8S4YRFOQLCP. 7 | # b7d23783e2c447c64204a6b33a40cb06ddab9f02ee16a484e93977aa21a9e7aa 8 | 2aa88e85203408c684ca95dc1f391c99:$2y$10$JRF2aO3ErJ9CTS.4b2Dnu.ychL67c.dQ33UopvujYjhGtibmeV0Ay 9 | # 426055d6d0d6b79b38faa64b76dcdaec02b9e5607444a4a5e7b60e30e148d5f5 10 | ee92794742387a7bd41e6b9d8765c9e7:$2y$10$Sg8rj10osHE3CI8BykCkBOsVPboK/16teppV6q3rabQmos2iwGrvK 11 | -------------------------------------------------------------------------------- /internal/auth/util.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package auth 16 | 17 | import "encoding/json" 18 | 19 | func ParseAuthSubject(subject string) (*AuthSubject, error) { 20 | var authSub AuthSubject 21 | err := json.Unmarshal([]byte(subject), &authSub) 22 | if err != nil { 23 | return nil, err 24 | } 25 | return &authSub, nil 26 | } 27 | -------------------------------------------------------------------------------- /internal/backend/kubernetes/namespace/kubernetes.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package namespace 16 | 17 | import ( 18 | "context" 19 | "time" 20 | 21 | "github.com/argoproj-labs/argocd-agent/internal/backend" 22 | "github.com/argoproj-labs/argocd-agent/internal/informer" 23 | ) 24 | 25 | var _ backend.Namespace = &KubernetesBackend{} 26 | 27 | // KubernetesBackend is an implementation of the backend.Namespace interface, which is used to track the state of namespaces 28 | // local to the agent/principal. KubernetesBackend is used by both the principal and agent components. 29 | type KubernetesBackend struct { 30 | // informer is used to watch for changes to the namespaces. 31 | informer informer.InformerInterface 32 | } 33 | 34 | func NewKubernetesBackend(nsInformer informer.InformerInterface) *KubernetesBackend { 35 | return &KubernetesBackend{ 36 | informer: nsInformer, 37 | } 38 | } 39 | 40 | func (be *KubernetesBackend) StartInformer(ctx context.Context) error { 41 | return be.informer.Start(ctx) 42 | } 43 | 44 | func (be *KubernetesBackend) EnsureSynced(timeout time.Duration) error { 45 | ctx, cancelFunc := context.WithTimeout(context.Background(), timeout) 46 | defer cancelFunc() 47 | return be.informer.WaitForSync(ctx) 48 | } 49 | -------------------------------------------------------------------------------- /internal/clock/clock.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | /* 16 | Package clock provides abstraction for the system clock. It is mainly used to 17 | ease testing time-based functions throughout the code. 18 | 19 | It only provides the most basic primitives Now(), Until() and Since(). 20 | */ 21 | package clock 22 | 23 | import "time" 24 | 25 | type Clock interface { 26 | Now() time.Time 27 | Until(t time.Time) time.Duration 28 | Since(t time.Time) time.Duration 29 | } 30 | 31 | var _ Clock = &standardClock{} 32 | var _ Clock = &seededClock{} 33 | 34 | type standardClock struct{} 35 | 36 | type seededClock struct { 37 | now time.Time 38 | } 39 | 40 | func StandardClock() *standardClock { 41 | return &standardClock{} 42 | } 43 | 44 | func (c *standardClock) Now() time.Time { 45 | return time.Now() 46 | } 47 | 48 | func (c *standardClock) Until(t time.Time) time.Duration { 49 | return time.Until(t) 50 | } 51 | 52 | func (c *standardClock) Since(t time.Time) time.Duration { 53 | return time.Since(t) 54 | } 55 | 56 | func SeededClock(now time.Time) *seededClock { 57 | return &seededClock{now: now} 58 | } 59 | 60 | func (c *seededClock) Now() time.Time { 61 | return c.now 62 | } 63 | 64 | func (c *seededClock) Until(t time.Time) time.Duration { 65 | return t.Sub(c.now) 66 | } 67 | 68 | func (c *seededClock) Since(t time.Time) time.Duration { 69 | return c.now.Sub(t) 70 | } 71 | 72 | func (c *seededClock) At(seed time.Time) { 73 | c.now = seed 74 | } 75 | -------------------------------------------------------------------------------- /internal/clock/clock_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package clock 16 | 17 | import ( 18 | "testing" 19 | "time" 20 | 21 | "github.com/stretchr/testify/assert" 22 | "github.com/stretchr/testify/require" 23 | ) 24 | 25 | func Test_StandadClock(t *testing.T) { 26 | clock := StandardClock() 27 | var now, cnow time.Time 28 | // For the unlikely event that now and cnow are being called in another 29 | // second. 30 | for { 31 | now = time.Now() 32 | cnow = clock.Now() 33 | if now.Unix() == cnow.Unix() { 34 | break 35 | } 36 | } 37 | } 38 | 39 | func Test_SeededClock(t *testing.T) { 40 | t.Run("Seeded clock", func(t *testing.T) { 41 | seed, err := time.Parse(time.RFC3339, "2023-12-12T00:01:00Z") 42 | require.NoError(t, err) 43 | clock := SeededClock(seed) 44 | now := clock.Now() 45 | assert.Equal(t, 2023, now.Year()) 46 | assert.Equal(t, time.Month(12), now.Month()) 47 | assert.Equal(t, 12, now.Day()) 48 | assert.Equal(t, 1, now.Minute()) 49 | seed, err = time.Parse(time.RFC3339, "2023-12-12T00:02:00Z") 50 | require.NoError(t, err) 51 | now = SeededClock(seed).Now() 52 | assert.Equal(t, 2, now.Minute()) 53 | }) 54 | } 55 | -------------------------------------------------------------------------------- /internal/config/constants.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package config provides functions and constants around the configuration of the 3 | various argocd-agent components. 4 | */ 5 | package config 6 | 7 | // SecretNamePrincipalCA is the name of the secret containing the TLS 8 | // configuration for the principal's Certificate Authority 9 | const SecretNamePrincipalCA = "argocd-agent-ca" 10 | 11 | // SecretNamePrincipalTLS is the name of the secret containing the TLS 12 | // configuration for the principal's gRPC service. 13 | const SecretNamePrincipalTLS = "argocd-agent-principal-tls" 14 | 15 | // SecretNameProxyTLS is the name of the secret containing the TLS 16 | // configuration for the principal's resource proxy. 17 | const SecretNameProxyTLS = "argocd-agent-resource-proxy-tls" 18 | 19 | // SecretNameAgentClientCert is the name of the secret containing the TLS 20 | // client certificate + key for an agent. 21 | const SecretNameAgentClientCert = "argocd-agent-client-tls" 22 | -------------------------------------------------------------------------------- /internal/filter/filter.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package filter 16 | 17 | import "k8s.io/apimachinery/pkg/runtime" 18 | 19 | type FilterType runtime.Object 20 | 21 | // ChangeFilterFunc is a function that compares old and new and returns false if the 22 | // agent should ignore this change. 23 | type ChangeFilterFunc[T FilterType] func(old T, new T) bool 24 | 25 | // AdmitFilterFunc is a function that returns true if the agent should handle a 26 | // given Application 27 | type AdmitFilterFunc[T FilterType] func(res T) bool 28 | 29 | // Chain is a chain of filters to decide whether a change should be 30 | // ignored by the agent. 31 | type Chain[T FilterType] struct { 32 | changeFilters []ChangeFilterFunc[T] 33 | admitFilters []AdmitFilterFunc[T] 34 | } 35 | 36 | // Append appends a filter function to the chain 37 | func (fc *Chain[T]) AppendChangeFilter(f ChangeFilterFunc[T]) { 38 | fc.changeFilters = append(fc.changeFilters, f) 39 | } 40 | 41 | // AppendAdmitFilter appends an admit filter function to the chain 42 | func (fc *Chain[T]) AppendAdmitFilter(f AdmitFilterFunc[T]) { 43 | fc.admitFilters = append(fc.admitFilters, f) 44 | } 45 | 46 | // ProcessChange runs all filters in the FilterChain and returns false if the change 47 | // should be ignored by the agent 48 | func (fc *Chain[T]) ProcessChange(old, new T) bool { 49 | for _, f := range fc.changeFilters { 50 | if !f(old, new) { 51 | return false 52 | } 53 | } 54 | return true 55 | } 56 | 57 | // Admit runs all admit filters in the FilterChain and returns true if the app 58 | // should be admitted 59 | func (fc *Chain[T]) Admit(app T) bool { 60 | for _, f := range fc.admitFilters { 61 | if !f(app) { 62 | return false 63 | } 64 | } 65 | return true 66 | } 67 | 68 | // NewFilterChain returns an instance of an empty FilterChain 69 | func NewFilterChain[T FilterType]() *Chain[T] { 70 | fc := &Chain[T]{} 71 | return fc 72 | } 73 | -------------------------------------------------------------------------------- /internal/filter/filter_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package filter 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" 21 | "github.com/sirupsen/logrus" 22 | "github.com/stretchr/testify/assert" 23 | ) 24 | 25 | func Test_Admit(t *testing.T) { 26 | fc := NewFilterChain[*v1alpha1.Application]() 27 | app := &v1alpha1.Application{} 28 | t.Run("Empty FilterChain always admits", func(t *testing.T) { 29 | assert.True(t, fc.Admit(app)) 30 | }) 31 | t.Run("Some filter admits", func(t *testing.T) { 32 | fc.AppendAdmitFilter(func(app *v1alpha1.Application) bool { 33 | return true 34 | }) 35 | assert.True(t, fc.Admit(app)) 36 | }) 37 | t.Run("Any filter blocks admission", func(t *testing.T) { 38 | fc.AppendAdmitFilter(func(app *v1alpha1.Application) bool { 39 | return false 40 | }) 41 | assert.False(t, fc.Admit(app)) 42 | }) 43 | } 44 | func Test_ProcessChange(t *testing.T) { 45 | fc := NewFilterChain[*v1alpha1.Application]() 46 | app := &v1alpha1.Application{} 47 | t.Run("Empty FilterChain always allows change", func(t *testing.T) { 48 | assert.True(t, fc.ProcessChange(app, app)) 49 | }) 50 | t.Run("Some filter admits", func(t *testing.T) { 51 | fc.AppendAdmitFilter(func(app *v1alpha1.Application) bool { 52 | return true 53 | }) 54 | assert.True(t, fc.Admit(app)) 55 | }) 56 | t.Run("Filter blocks change processing", func(t *testing.T) { 57 | fc.AppendChangeFilter(func(old, new *v1alpha1.Application) bool { 58 | return false 59 | }) 60 | assert.False(t, fc.ProcessChange(app, app)) 61 | }) 62 | } 63 | 64 | func init() { 65 | logrus.SetLevel(logrus.TraceLevel) 66 | } 67 | -------------------------------------------------------------------------------- /internal/grpcutil/address.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package grpcutil 16 | 17 | import ( 18 | "context" 19 | 20 | "google.golang.org/grpc/peer" 21 | ) 22 | 23 | const unknownAddress = "unknown" 24 | 25 | // AddressFromContext returns the peer's address as string from the context. 26 | // If there is no peer information in the context, returns "unknown". 27 | func AddressFromContext(ctx context.Context) string { 28 | c, ok := peer.FromContext(ctx) 29 | if !ok { 30 | return unknownAddress 31 | } 32 | return c.Addr.String() 33 | } 34 | -------------------------------------------------------------------------------- /internal/grpcutil/address_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package grpcutil 16 | 17 | import ( 18 | "context" 19 | "net" 20 | "net/netip" 21 | "testing" 22 | 23 | "github.com/stretchr/testify/assert" 24 | "google.golang.org/grpc/peer" 25 | ) 26 | 27 | func Test_AddressFromContext(t *testing.T) { 28 | const myAddr = "127.0.0.1:23" 29 | p := peer.Peer{Addr: net.TCPAddrFromAddrPort(netip.MustParseAddrPort(myAddr))} 30 | ctx := peer.NewContext(context.Background(), &p) 31 | t.Run("Get valid peer address from context", func(t *testing.T) { 32 | assert.Equal(t, myAddr, AddressFromContext(ctx)) 33 | }) 34 | t.Run("Get invalid peer address from context", func(t *testing.T) { 35 | assert.Equal(t, unknownAddress, AddressFromContext(context.Background())) 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /internal/grpcutil/errors.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package grpcutil 16 | 17 | import ( 18 | "errors" 19 | "io" 20 | 21 | "google.golang.org/grpc/codes" 22 | "google.golang.org/grpc/status" 23 | ) 24 | 25 | var reconnectableErrors map[codes.Code]bool = map[codes.Code]bool{ 26 | codes.Unavailable: true, 27 | codes.Unauthenticated: true, 28 | codes.Canceled: true, 29 | } 30 | 31 | func NeedReconnectOnError(err error) bool { 32 | if errors.Is(err, io.EOF) { 33 | return true 34 | } 35 | status, ok := status.FromError(err) 36 | if !ok { 37 | return false 38 | } 39 | v, ok := reconnectableErrors[status.Code()] 40 | if ok && v { 41 | return true 42 | } 43 | return false 44 | } 45 | -------------------------------------------------------------------------------- /internal/grpcutil/errors_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package grpcutil 16 | 17 | import ( 18 | "fmt" 19 | "io" 20 | "testing" 21 | 22 | "github.com/stretchr/testify/assert" 23 | "google.golang.org/grpc/codes" 24 | "google.golang.org/grpc/status" 25 | ) 26 | 27 | func Test_NeedReconnectOnError(t *testing.T) { 28 | t.Run("Need reconnect on EOF", func(t *testing.T) { 29 | assert.True(t, NeedReconnectOnError(io.EOF)) 30 | }) 31 | t.Run("No reconnect on irrelevant error", func(t *testing.T) { 32 | assert.False(t, NeedReconnectOnError(fmt.Errorf("some error"))) 33 | }) 34 | t.Run("Need reconnect on gRPC error codes", func(t *testing.T) { 35 | for k := range reconnectableErrors { 36 | err := status.Error(k, "") 37 | assert.True(t, NeedReconnectOnError(err)) 38 | } 39 | }) 40 | t.Run("No reconnect on irrelevant gRPC error code", func(t *testing.T) { 41 | err := status.Error(codes.Internal, "") 42 | assert.False(t, NeedReconnectOnError(err)) 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /internal/issuer/issuer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package issuer 16 | 17 | import ( 18 | "time" 19 | 20 | "github.com/golang-jwt/jwt/v5" 21 | ) 22 | 23 | type Claims interface { 24 | jwt.Claims 25 | } 26 | 27 | type Issuer interface { 28 | IssueAccessToken(client string, exp time.Duration) (string, error) 29 | IssueRefreshToken(client string, exp time.Duration) (string, error) 30 | ValidateAccessToken(token string) (Claims, error) 31 | ValidateRefreshToken(token string) (Claims, error) 32 | } 33 | -------------------------------------------------------------------------------- /internal/issuer/mocks/JwtIssuerOption.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v2.43.0. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import ( 6 | issuer "github.com/argoproj-labs/argocd-agent/internal/issuer" 7 | mock "github.com/stretchr/testify/mock" 8 | ) 9 | 10 | // JwtIssuerOption is an autogenerated mock type for the JwtIssuerOption type 11 | type JwtIssuerOption struct { 12 | mock.Mock 13 | } 14 | 15 | type JwtIssuerOption_Expecter struct { 16 | mock *mock.Mock 17 | } 18 | 19 | func (_m *JwtIssuerOption) EXPECT() *JwtIssuerOption_Expecter { 20 | return &JwtIssuerOption_Expecter{mock: &_m.Mock} 21 | } 22 | 23 | // Execute provides a mock function with given fields: i 24 | func (_m *JwtIssuerOption) Execute(i *issuer.JwtIssuer) error { 25 | ret := _m.Called(i) 26 | 27 | if len(ret) == 0 { 28 | panic("no return value specified for Execute") 29 | } 30 | 31 | var r0 error 32 | if rf, ok := ret.Get(0).(func(*issuer.JwtIssuer) error); ok { 33 | r0 = rf(i) 34 | } else { 35 | r0 = ret.Error(0) 36 | } 37 | 38 | return r0 39 | } 40 | 41 | // JwtIssuerOption_Execute_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Execute' 42 | type JwtIssuerOption_Execute_Call struct { 43 | *mock.Call 44 | } 45 | 46 | // Execute is a helper method to define mock.On call 47 | // - i *issuer.JwtIssuer 48 | func (_e *JwtIssuerOption_Expecter) Execute(i interface{}) *JwtIssuerOption_Execute_Call { 49 | return &JwtIssuerOption_Execute_Call{Call: _e.mock.On("Execute", i)} 50 | } 51 | 52 | func (_c *JwtIssuerOption_Execute_Call) Run(run func(i *issuer.JwtIssuer)) *JwtIssuerOption_Execute_Call { 53 | _c.Call.Run(func(args mock.Arguments) { 54 | run(args[0].(*issuer.JwtIssuer)) 55 | }) 56 | return _c 57 | } 58 | 59 | func (_c *JwtIssuerOption_Execute_Call) Return(_a0 error) *JwtIssuerOption_Execute_Call { 60 | _c.Call.Return(_a0) 61 | return _c 62 | } 63 | 64 | func (_c *JwtIssuerOption_Execute_Call) RunAndReturn(run func(*issuer.JwtIssuer) error) *JwtIssuerOption_Execute_Call { 65 | _c.Call.Return(run) 66 | return _c 67 | } 68 | 69 | // NewJwtIssuerOption creates a new instance of JwtIssuerOption. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. 70 | // The first argument is typically a *testing.T value. 71 | func NewJwtIssuerOption(t interface { 72 | mock.TestingT 73 | Cleanup(func()) 74 | }) *JwtIssuerOption { 75 | mock := &JwtIssuerOption{} 76 | mock.Mock.Test(t) 77 | 78 | t.Cleanup(func() { mock.AssertExpectations(t) }) 79 | 80 | return mock 81 | } 82 | -------------------------------------------------------------------------------- /internal/labels/parser.go: -------------------------------------------------------------------------------- 1 | package labels 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | // StringsToMap parses key=value pairs in labels and returns a map, where the 9 | // entries are populated from the parsed strings. 10 | func StringsToMap(labels []string) (map[string]string, error) { 11 | ret := make(map[string]string) 12 | for _, llent := range labels { 13 | larr := strings.SplitN(llent, "=", 2) 14 | if len(larr) != 2 || larr[1] == "" { 15 | return nil, fmt.Errorf("invalid label '%s': has no value", llent) 16 | } 17 | ret[larr[0]] = larr[1] 18 | } 19 | return ret, nil 20 | } 21 | -------------------------------------------------------------------------------- /internal/labels/parser_test.go: -------------------------------------------------------------------------------- 1 | package labels 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func Test_StringsToMap(t *testing.T) { 10 | t.Run("Single valid label", func(t *testing.T) { 11 | m, err := StringsToMap([]string{"foo=bar"}) 12 | assert.NoError(t, err) 13 | assert.Equal(t, "bar", m["foo"]) 14 | }) 15 | t.Run("Preserve equal char in values", func(t *testing.T) { 16 | m, err := StringsToMap([]string{"foo=bar=baz"}) 17 | assert.NoError(t, err) 18 | assert.Equal(t, "bar=baz", m["foo"]) 19 | }) 20 | t.Run("Multiple valid labels", func(t *testing.T) { 21 | m, err := StringsToMap([]string{"foo=bar", "bar=foo"}) 22 | assert.NoError(t, err) 23 | assert.Equal(t, "bar", m["foo"]) 24 | assert.Equal(t, "foo", m["bar"]) 25 | }) 26 | t.Run("Label without value", func(t *testing.T) { 27 | m, err := StringsToMap([]string{"foo=", "bar=foo"}) 28 | assert.Error(t, err) 29 | assert.Nil(t, m) 30 | }) 31 | t.Run("Malformed syntax", func(t *testing.T) { 32 | m, err := StringsToMap([]string{"foo"}) 33 | assert.Error(t, err) 34 | assert.Nil(t, m) 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /internal/manager/manager.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package manager 16 | 17 | type ManagerRole int 18 | type ManagerMode int 19 | 20 | const ( 21 | ManagerRoleUnset ManagerRole = iota 22 | ManagerRolePrincipal 23 | ManagerRoleAgent 24 | ) 25 | 26 | const ( 27 | ManagerModeUnset ManagerMode = iota 28 | ManagerModeAutonomous 29 | ManagerModeManaged 30 | ) 31 | 32 | const ( 33 | // SourceUIDAnnotation is an annotation that represents the UID of the source resource. 34 | // It is added to the resources managed on the target. 35 | SourceUIDAnnotation = "argocd.argoproj.io/source-uid" 36 | ) 37 | 38 | type Manager interface { 39 | SetRole(role ManagerRole) 40 | SetMode(role ManagerRole) 41 | } 42 | 43 | // IsPrincipal returns true if the manager role is principal 44 | func (r ManagerRole) IsPrincipal() bool { 45 | return r == ManagerRolePrincipal 46 | } 47 | 48 | // IsAgent returns true if the manager role is agent 49 | func (r ManagerRole) IsAgent() bool { 50 | return r == ManagerRoleAgent 51 | } 52 | 53 | // IsAutonomous returns true if the manager mode is autonomous 54 | func (m ManagerMode) IsAutonomous() bool { 55 | return m == ManagerModeAutonomous 56 | } 57 | 58 | // IsManaged returns true if the manager mode is managed mode 59 | func (m ManagerMode) IsManaged() bool { 60 | return m == ManagerModeManaged 61 | } 62 | -------------------------------------------------------------------------------- /internal/manager/manager_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package manager 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/stretchr/testify/assert" 21 | ) 22 | 23 | func Test_IsPrincipal(t *testing.T) { 24 | r := ManagerRoleAgent 25 | assert.True(t, r.IsAgent()) 26 | assert.False(t, r.IsPrincipal()) 27 | } 28 | func Test_IsAgent(t *testing.T) { 29 | r := ManagerRolePrincipal 30 | assert.True(t, r.IsPrincipal()) 31 | assert.False(t, r.IsAgent()) 32 | } 33 | 34 | func Test_IsAutonomous(t *testing.T) { 35 | m := ManagerModeAutonomous 36 | assert.True(t, m.IsAutonomous()) 37 | assert.False(t, m.IsManaged()) 38 | } 39 | 40 | func Test_IsManaged(t *testing.T) { 41 | m := ManagerModeManaged 42 | assert.True(t, m.IsManaged()) 43 | assert.False(t, m.IsAutonomous()) 44 | } 45 | -------------------------------------------------------------------------------- /internal/metrics/server.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package metrics 16 | 17 | import ( 18 | "fmt" 19 | "net/http" 20 | neturl "net/url" 21 | 22 | "github.com/prometheus/client_golang/prometheus/promhttp" 23 | "github.com/sirupsen/logrus" 24 | ) 25 | 26 | type MetricsServerOptions struct { 27 | host string 28 | port int 29 | path string 30 | } 31 | 32 | type MetricsServerOption func(*MetricsServerOptions) 33 | 34 | func listener(o *MetricsServerOptions) string { 35 | l := "" 36 | if o.host != "" { 37 | l += o.host 38 | } else { 39 | l += "0.0.0.0" 40 | } 41 | l += fmt.Sprintf(":%d", o.port) 42 | return l 43 | } 44 | 45 | func url(o *MetricsServerOptions) string { 46 | u := neturl.URL{} 47 | u.Scheme = "http" 48 | h := "" 49 | if o.host != "" { 50 | h = o.host 51 | } else { 52 | h = "0.0.0.0" 53 | } 54 | u.Host = fmt.Sprintf("%s:%d", h, o.port) 55 | u.Path = "/metrics" 56 | return u.String() 57 | } 58 | 59 | func WithListener(hostname string, port int) MetricsServerOption { 60 | return func(o *MetricsServerOptions) { 61 | if hostname != "" { 62 | o.host = hostname 63 | } 64 | o.port = port 65 | } 66 | } 67 | 68 | // StartMetricsServer starts the metrics server in a separate go routine and 69 | // returns an error channel. 70 | func StartMetricsServer(opts ...MetricsServerOption) chan error { 71 | config := &MetricsServerOptions{ 72 | host: "", 73 | port: 8080, 74 | path: "/metrics", 75 | } 76 | for _, o := range opts { 77 | o(config) 78 | } 79 | errCh := make(chan error) 80 | logrus.Infof("Starting metrics server on %s", url(config)) 81 | go func() { 82 | sm := http.NewServeMux() 83 | sm.Handle(config.path, promhttp.Handler()) 84 | errCh <- http.ListenAndServe(listener(config), sm) 85 | }() 86 | return errCh 87 | } 88 | -------------------------------------------------------------------------------- /internal/metrics/server_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package metrics 16 | 17 | import ( 18 | "io" 19 | "net/http" 20 | "testing" 21 | "time" 22 | 23 | "github.com/stretchr/testify/assert" 24 | "github.com/stretchr/testify/require" 25 | ) 26 | 27 | func fetchMetricsOutput(t *testing.T) string { 28 | t.Helper() 29 | r, err := http.Get("http://127.0.0.1:31337/metrics") 30 | require.NoError(t, err) 31 | require.Equal(t, 200, r.StatusCode) 32 | body, err := io.ReadAll(r.Body) 33 | r.Body.Close() 34 | require.NoError(t, err) 35 | return string(body) 36 | } 37 | 38 | func Test_MetricsServer(t *testing.T) { 39 | t.Run("Start metrics server", func(t *testing.T) { 40 | errCh := StartMetricsServer(WithListener("127.0.0.1", 31337)) 41 | NewPrincipalMetrics() 42 | ticker := time.NewTicker(time.Second) 43 | select { 44 | case err := <-errCh: 45 | assert.NoError(t, err) 46 | case <-ticker.C: 47 | fetchMetricsOutput(t) 48 | } 49 | }) 50 | } 51 | -------------------------------------------------------------------------------- /internal/namedlock/namelock_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package namedlock 16 | 17 | import ( 18 | "testing" 19 | "time" 20 | 21 | "github.com/stretchr/testify/assert" 22 | ) 23 | 24 | func Test_Lock(t *testing.T) { 25 | nl := NewNamedLock() 26 | assert.True(t, nl.TryLock("foo")) 27 | assert.False(t, nl.TryLock("foo")) 28 | assert.True(t, nl.TryLock("bar")) 29 | testch := make(chan struct{}) 30 | go func() { 31 | // We have no assertions here, other than being able to acquire locks 32 | nl.Unlock("foo") 33 | nl.Unlock("bar") 34 | nl.RLock("foo") 35 | nl.RLock("foo") 36 | assert.False(t, nl.TryLock("foo")) 37 | nl.RUnlock("foo") 38 | nl.RUnlock("foo") 39 | nl.Lock("foo") 40 | assert.False(t, nl.TryRLock("foo")) 41 | nl.Unlock("foo") 42 | assert.True(t, nl.TryRLock("foo")) 43 | nl.RUnlock("foo") 44 | close(testch) 45 | }() 46 | timeout := time.NewTimer(1 * time.Second) 47 | for { 48 | select { 49 | case <-testch: 50 | return 51 | case <-timeout.C: 52 | t.Fatal("timeout reached") 53 | } 54 | time.Sleep(50 * time.Millisecond) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /internal/session/session.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package session 16 | 17 | import ( 18 | "context" 19 | "fmt" 20 | 21 | "github.com/argoproj-labs/argocd-agent/pkg/types" 22 | "k8s.io/apimachinery/pkg/api/validation" 23 | ) 24 | 25 | /* 26 | Package session contains various functions to access and manipulate session 27 | data. 28 | */ 29 | 30 | // ClientIDFromContext returns the client ID stored in context ctx. If there 31 | // is no client ID in the context, or the client ID is invalid, returns an 32 | // error. 33 | func ClientIDFromContext(ctx context.Context) (string, error) { 34 | val := ctx.Value(types.ContextAgentIdentifier) 35 | clientID, ok := val.(string) 36 | if !ok { 37 | return "", fmt.Errorf("no client identifier found in context") 38 | } 39 | if !IsValidClientID(clientID) { 40 | return "", fmt.Errorf("invalid client identifier: %s", clientID) 41 | } 42 | return clientID, nil 43 | } 44 | 45 | // ClientModeFromContext returns the client mode stored in context ctx. Returns an 46 | // error if there is no client mode in the ctx. 47 | func ClientModeFromContext(ctx context.Context) (string, error) { 48 | val := ctx.Value(types.ContextAgentMode) 49 | clientMode, ok := val.(string) 50 | if !ok { 51 | return "", fmt.Errorf("no client mode found in context") 52 | } 53 | return clientMode, nil 54 | } 55 | 56 | // ClientInfoToContext returns a copy of context ctx with the clientID and clientMode stored 57 | func ClientInfoToContext(ctx context.Context, clientID, clientMode string) context.Context { 58 | clientCtx := context.WithValue(ctx, types.ContextAgentIdentifier, clientID) 59 | return context.WithValue(clientCtx, types.ContextAgentMode, clientMode) 60 | } 61 | 62 | // IsValidClientID returns true if the string s is considered a valid client 63 | // identifier. 64 | func IsValidClientID(s string) bool { 65 | if errs := validation.NameIsDNSSubdomain(s, false); len(errs) > 0 { 66 | return false 67 | } 68 | return true 69 | } 70 | -------------------------------------------------------------------------------- /internal/session/session_test.go: -------------------------------------------------------------------------------- 1 | package session 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func Test_ClientInfoInContext(t *testing.T) { 11 | t.Run("Successfully extract client ID and mode", func(t *testing.T) { 12 | ctx := ClientInfoToContext(context.Background(), "agent", "managed") 13 | a, err := ClientIDFromContext(ctx) 14 | assert.NoError(t, err) 15 | assert.Equal(t, "agent", a) 16 | m, err := ClientModeFromContext(ctx) 17 | assert.NoError(t, err) 18 | assert.Equal(t, "managed", m) 19 | }) 20 | t.Run("No client ID in context", func(t *testing.T) { 21 | a, err := ClientIDFromContext(context.Background()) 22 | assert.ErrorContains(t, err, "no client identifier") 23 | assert.Empty(t, a) 24 | }) 25 | t.Run("No client mode in context", func(t *testing.T) { 26 | a, err := ClientModeFromContext(context.Background()) 27 | assert.ErrorContains(t, err, "no client mode found in context") 28 | assert.Empty(t, a) 29 | }) 30 | t.Run("Invalid client ID in context", func(t *testing.T) { 31 | ctx := ClientInfoToContext(context.Background(), "ag_ent", "managed") 32 | a, err := ClientIDFromContext(ctx) 33 | assert.ErrorContains(t, err, "invalid client identifier") 34 | assert.Empty(t, a) 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /internal/tlsutil/testdata/001_test_cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDZTCCAk2gAwIBAgIUFAk2kiIEyjS0nCZd/ymtM1qKlfYwDQYJKoZIhvcNAQEL 3 | BQAwQjELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEcMBoGA1UE 4 | CgwTRGVmYXVsdCBDb21wYW55IEx0ZDAeFw0yNDEyMTcxNDE5MDhaFw0yNTEyMTcx 5 | NDE5MDhaMEIxCzAJBgNVBAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAa 6 | BgNVBAoME0RlZmF1bHQgQ29tcGFueSBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IB 7 | DwAwggEKAoIBAQCiKV5G7Pu9aC/ZapHuu3yAC6XrVNAgcPHVK0/oQZWCn9/w+3gO 8 | 8LZKo9x0rgz7zrlNjg+lR2lQIQ37Zk6b2tVBhl2O9Qm8HShoY17etBuJJI2a7Ru7 9 | j4IW+GPDON2POiQwFQxoXo3AKyCEzohV7vJ0ShF0M5viJA6Y5jcVSzkQXrDqt1m6 10 | hLA3mVxkFZ76qCSnOzMoA80+hHWpI64rgZKsKlWQPWe9fE+oIp/JyKU4TXhP/Kua 11 | 6ulkQ31dP0gzpANWaROvU3NUy0O+pxyGqc6lpFD94DqqTV8EVh1CknA3KO3pfmX+ 12 | skHm5jL4DYdPn8ciNibRETh+8AhWjOQcNXnFAgMBAAGjUzBRMB0GA1UdDgQWBBQL 13 | rIsnR3OoNtm7InHaaaJhYhFG2zAfBgNVHSMEGDAWgBQLrIsnR3OoNtm7InHaaaJh 14 | YhFG2zAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQB/EECtLv5D 15 | 7gFkz3NxcaJfjvGPO2vTSJx79T0ndYKwXqqGisOXfY2SVkq3B03HMRCeTNWJmEie 16 | Gd5zxdBw/lVYFmhJmBzGchD7t4eDx0Yavm8kyc2oU1HAWgZBxtye54ZjAYqBFsrw 17 | qYRiJH5Zr1+8Xacc4usoNGxeGDZWtNrz/qku7pb1pdBGQzUADI+d3DG8+XfhMZtr 18 | jpuq+kZfR/GvFKxHMwyjRw1vwRDL0FYbDtcIbqqJTsR9lxYLhtYbOSzCkGqjmbpQ 19 | rUZ4rsIYeXh+N7vbIVqYjjbmA6fB1N0S3/svVuHtzXxfbuzxBcdDdPrZr3VmoZIh 20 | et6RvI7KUqx+ 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /internal/tlsutil/testdata/001_test_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCiKV5G7Pu9aC/Z 3 | apHuu3yAC6XrVNAgcPHVK0/oQZWCn9/w+3gO8LZKo9x0rgz7zrlNjg+lR2lQIQ37 4 | Zk6b2tVBhl2O9Qm8HShoY17etBuJJI2a7Ru7j4IW+GPDON2POiQwFQxoXo3AKyCE 5 | zohV7vJ0ShF0M5viJA6Y5jcVSzkQXrDqt1m6hLA3mVxkFZ76qCSnOzMoA80+hHWp 6 | I64rgZKsKlWQPWe9fE+oIp/JyKU4TXhP/Kua6ulkQ31dP0gzpANWaROvU3NUy0O+ 7 | pxyGqc6lpFD94DqqTV8EVh1CknA3KO3pfmX+skHm5jL4DYdPn8ciNibRETh+8AhW 8 | jOQcNXnFAgMBAAECggEADOfH61MZkcWvO9lXCy3RRwt7mpKhtwM7a8tKTQydznXU 9 | x66WVnINFTxHOONP1eEU2Y7lwIdCDVt5a7mE+12wvR8+u42AHIAhjXU4a/bffiGc 10 | 7L3UCDBFRSNiz9BAabv8fKB1iqouaSMa2mw+vhVfJvwXZC0QnzBewl0H+IzdBL1e 11 | g4JLylJk1k0YIStzI1qDP32t78mXuNvHPeQqDzZaFPHcAP2okMCCVMiCspnfa4RO 12 | lvpW6IC1rr0cJYmYqkTFDft2ny/OaSKNc7bw9/OKwP+SaZywXQcZfMeS3iDpHdgs 13 | 1PbXpTjGayPU1zFLac4ChdxuPMIKZ1bWD8pUC1dWvQKBgQDjCvYhNGZn7zf3dBH8 14 | hIzPauDcR/Bawcxqd8Wgbvu/zTbicxdLni0ECrRz625eJatkG9GdRGvtRlWzZUbC 15 | FYSEV1SnfJkmaCl3tcY3AZpnb/aEgb+kHQl0vMUgciCOsDjYTYz47is+7Yp1qXor 16 | QmSK0kTcE3KyjVipukEuSImvLwKBgQC22AJObwHv9wEfIPRBrfbAxrRrDuQ0V6tk 17 | S2TlHCpFCh00ksBuj7jmvDq92hqAZhqSeuZtZx8EyFam6HT0l/EYzTgG9RMxaHhP 18 | PIshry5S0FK2pnsZIE+vpdXRr1uFFVRPqtj4HP17jvvEW3pQXXYNtMs3FPkfTyGq 19 | XR+PZRGJSwKBgCDng8hIKddCSiAoyDqKk0W0PaZvHpxondGITjH0I7Qmb5/eAjBJ 20 | WkjNrF1ob3RhjTdS+MwMEIAww1behKS4LZ5ocbJcUm3Ihsn8pB9wsgnvphCKJVYJ 21 | h0dN3FvZbnJ/g52Fj7q7+bSDBKAM0dHXK28bDjO+9c5+wazHe47ToHCtAoGAZqZk 22 | vRXzN34rogdFOe5pnpavyX7lvUEO1tLBBSNH09S2ysIsyKVlgBxiuh1NTZKFDoFz 23 | Bi6jqnKyuye8KWl4EJ19++Hw8YceLBXoYnPQBOwx05spdtS+B/WJUhwpvFBaMhPP 24 | lZPo90oxrG5S//VIhq9ee0EKD3rEgrmfM0jhjHsCgYAH7V5L3HnhRdhMPb5Ggvxm 25 | dH00H5t52OnJTxiCjqOFl02BdsRWkweyPFSecACC64oEDJ2OEZvTP9tAj+OUy3Sf 26 | /pPeVFagWYvijo5fOjKreqViwB8ipFk6syZTLhCyfN9um1qfjuad1IjGwU3Cwklh 27 | 0RiX8twiB5IDY3fDvuBB1Q== 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /internal/version/version.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package version 16 | 17 | import ( 18 | "encoding/json" 19 | "runtime" 20 | 21 | "gopkg.in/yaml.v3" 22 | ) 23 | 24 | var version = "99.9.9-unreleased" 25 | var gitRevision = "unknown" 26 | var gitStatus = "unknown" 27 | 28 | type versionInformation struct { 29 | Name string `json:"name"` 30 | Component string `json:"component"` 31 | Version string `json:"version"` 32 | GitRevision string `json:"gitRevision" yaml:"gitRevision"` 33 | GitStatus string `json:"gitStatus" yaml:"gitStatus"` 34 | GoVersion string `json:"goVersion" yaml:"goVersion"` 35 | } 36 | 37 | type Version struct { 38 | v versionInformation 39 | } 40 | 41 | func New(name, component string) *Version { 42 | v := versionInformation{ 43 | Name: name, 44 | Component: component, 45 | Version: version, 46 | GitRevision: gitRevision, 47 | GitStatus: gitStatus, 48 | GoVersion: runtime.Version(), 49 | } 50 | return &Version{v: v} 51 | } 52 | 53 | func (v *Version) QualifiedVersion() string { 54 | return v.v.Name + "-" + v.v.Version 55 | } 56 | 57 | func (v *Version) Version() string { 58 | return version 59 | } 60 | 61 | func (v *Version) Name() string { 62 | return v.v.Name 63 | } 64 | 65 | func (v *Version) Component() string { 66 | return v.v.Component 67 | } 68 | 69 | func (v *Version) GitRevision() string { 70 | return v.v.GitRevision 71 | } 72 | 73 | func (v *Version) GitStatus() string { 74 | return v.v.GitStatus 75 | } 76 | 77 | func (v *Version) YAML() string { 78 | b, err := yaml.Marshal(v.v) 79 | if err != nil { 80 | return "error: " + err.Error() 81 | } 82 | return string(b) 83 | } 84 | 85 | func (v *Version) JSON(indent bool) string { 86 | var b []byte 87 | var err error 88 | if indent { 89 | b, err = json.MarshalIndent(v.v, "", " ") 90 | } else { 91 | b, err = json.Marshal(v.v) 92 | 93 | } 94 | if err != nil { 95 | return "{}" 96 | } 97 | return string(b) 98 | } 99 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: argocd-agent 2 | repo_url: https://github.com/argoproj-labs/argocd-agent 3 | strict: true 4 | theme: 5 | name: material 6 | palette: 7 | primary: teal 8 | logo: assets/logo.png 9 | favicon: "assets/favicon.png" 10 | markdown_extensions: 11 | - codehilite 12 | - admonition 13 | - tables 14 | - toc: 15 | permalink: true 16 | nav: 17 | - Overview: index.md 18 | - Features: 19 | - Overview: features/index.md 20 | - Concepts: 21 | - Components & terminology: concepts/components-terminology.md 22 | - General architecture: concepts/architecture.md 23 | - Integration with Argo CD: concepts/argocd-integration.md 24 | - Agent modes: 25 | - Overview: concepts/agent-modes/index.md 26 | - Managed mode: concepts/agent-modes/managed.md 27 | - Autonomous mode: concepts/agent-modes/autonomous.md 28 | - Getting started: 29 | - Overview: getting-started/index.md 30 | - Kubernetes: getting-started/kubernetes/index.md 31 | - OpenShift: getting-started/openshift/index.md 32 | - OCM-io: getting-started/ocm-io/index.md 33 | - Configuration: 34 | - Principal: configuration/principal/index.md 35 | - Agent: configuration/agent/index.md 36 | - Operations: 37 | - Metrics: operations/metrics.md 38 | - Profiling: operations/profiling.md 39 | - Contributing: 40 | - Overview: contributing/index.md 41 | - Local development: contributing/local.md 42 | - Coding guidelines: contributing/code.md 43 | - Documentation guidelines: contributing/docs.md 44 | - Submitting PRs: contributing/prs.md 45 | - Code reviewing guide: contributing/review.md 46 | - FAQ: faq/index.md -------------------------------------------------------------------------------- /pkg/types/types.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package types 16 | 17 | const ( 18 | AuthResultOK string = "ok" 19 | AuthResultUnauthorized string = "unauthorized" 20 | ) 21 | 22 | type AgentMode string 23 | 24 | const agentModeManaged = "managed" 25 | const agentModeAutonomous = "autonomous" 26 | const agentModeUnknown = "unknown" 27 | 28 | const ( 29 | AgentModeUnknown AgentMode = "" 30 | AgentModeManaged AgentMode = "managed" 31 | AgentModeAutonomous AgentMode = "autonomous" 32 | ) 33 | 34 | func (m AgentMode) String() string { 35 | switch m { 36 | case AgentModeManaged: 37 | return agentModeManaged 38 | case AgentModeAutonomous: 39 | return agentModeAutonomous 40 | } 41 | return agentModeUnknown 42 | } 43 | 44 | func (m AgentMode) IsAutonomous() bool { 45 | return m == AgentModeAutonomous 46 | } 47 | 48 | func (m AgentMode) IsManaged() bool { 49 | return m == AgentModeManaged 50 | } 51 | 52 | func AgentModeFromString(mode string) AgentMode { 53 | switch mode { 54 | case agentModeManaged: 55 | return AgentModeManaged 56 | case agentModeAutonomous: 57 | return AgentModeAutonomous 58 | default: 59 | return AgentModeUnknown 60 | } 61 | } 62 | 63 | type EventContextKey string 64 | 65 | func (k EventContextKey) String() string { 66 | return string(k) 67 | } 68 | 69 | const ( 70 | ContextAgentIdentifier EventContextKey = "agent_name" 71 | ContextAgentMode EventContextKey = "agent_mode" 72 | ) 73 | 74 | type Agent struct { 75 | name string 76 | mode string 77 | } 78 | 79 | func (a Agent) Name() string { 80 | return a.name 81 | } 82 | 83 | func (a Agent) Mode() string { 84 | return a.mode 85 | } 86 | 87 | func NewAgent(name, mode string) Agent { 88 | return Agent{ 89 | name: name, 90 | mode: mode, 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /pkg/types/types_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package types 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/stretchr/testify/assert" 21 | ) 22 | 23 | func Test_AgentMode_String(t *testing.T) { 24 | assert.Equal(t, agentModeAutonomous, AgentModeAutonomous.String()) 25 | assert.Equal(t, agentModeManaged, AgentModeManaged.String()) 26 | assert.Equal(t, agentModeUnknown, AgentModeUnknown.String()) 27 | } 28 | 29 | func Test_AgentMode_FromString(t *testing.T) { 30 | assert.Equal(t, AgentModeAutonomous, AgentModeFromString(agentModeAutonomous)) 31 | assert.Equal(t, AgentModeManaged, AgentModeFromString(agentModeManaged)) 32 | assert.Equal(t, AgentModeUnknown, AgentModeFromString("whatever")) 33 | } 34 | -------------------------------------------------------------------------------- /principal/apis/auth/auth.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | option go_package = "github.com/argoproj-labs/argocd-agent/pkg/api/grpc/authapi"; 17 | 18 | package authapi; 19 | 20 | import "google/api/annotations.proto"; 21 | 22 | message AuthRequest { 23 | // Method specifies the type of authentication to use. The value is implementation specific. 24 | string method = 1; 25 | // Credentials specifies the credentials to use for authentication in a key/value fashion. 26 | map credentials = 2; 27 | // Agent's mode of operation, usually either managed or autonomous 28 | string mode = 3; 29 | } 30 | 31 | message AuthResponse { 32 | string accessToken = 1; 33 | string refreshToken = 2; 34 | } 35 | 36 | message RefreshTokenRequest { 37 | string refreshToken = 1; 38 | } 39 | 40 | service Authentication { 41 | rpc Authenticate(AuthRequest) returns (AuthResponse) { 42 | option (google.api.http) = { 43 | post: "/api/v1/auth/authenticate" 44 | body: "auth" 45 | }; 46 | } 47 | 48 | rpc RefreshToken(RefreshTokenRequest) returns (AuthResponse) { 49 | option (google.api.http) = { 50 | post: "/api/v1/auth/refresh" 51 | body: "refresh" 52 | }; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /principal/apis/auth/options.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package auth 16 | -------------------------------------------------------------------------------- /principal/apis/eventstream/eventstream.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | option go_package = "github.com/argoproj-labs/argocd-agent/pkg/api/grpc/eventstreamapi"; 18 | 19 | package eventstreamapi; 20 | 21 | import "google/api/annotations.proto"; 22 | 23 | // The following imports require dependencies to be vendored in 24 | import "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1/generated.proto"; 25 | import "github.com/cloudevents/sdk-go/binding/format/protobuf/v2/pb/cloudevent.proto"; 26 | 27 | // Event describes an event 28 | message Event { 29 | io.cloudevents.v1.CloudEvent event = 1; 30 | } 31 | 32 | message PushSummary { 33 | string result = 1; 34 | int32 received = 2; 35 | int32 processed = 3; 36 | } 37 | 38 | message PingRequest { 39 | 40 | } 41 | 42 | message PongReply { 43 | 44 | } 45 | 46 | service EventStream { 47 | rpc Subscribe(stream Event) returns (stream Event) { 48 | option (google.api.http).get = "/api/v1/events/stream"; 49 | } 50 | 51 | rpc Push(stream Event) returns (PushSummary) { 52 | option (google.api.http).get = "/api/v1/events/push"; 53 | } 54 | 55 | rpc Ping(PingRequest) returns (PongReply) { 56 | option (google.api.http).get = "/api/v1/ping"; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /principal/apis/version/version.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package version 16 | 17 | import ( 18 | "context" 19 | 20 | "github.com/argoproj-labs/argocd-agent/internal/version" 21 | "github.com/argoproj-labs/argocd-agent/pkg/api/grpc/versionapi" 22 | "google.golang.org/grpc/codes" 23 | "google.golang.org/grpc/status" 24 | ) 25 | 26 | var unauthenticatedMethods map[string]bool = map[string]bool{ 27 | "/versionapi.Version/Version": true, 28 | } 29 | 30 | type server struct { 31 | versionapi.UnimplementedVersionServer 32 | authfunc func(context.Context) (context.Context, error) 33 | version *version.Version 34 | } 35 | 36 | func NewServer(authfunc func(context.Context) (context.Context, error)) *server { 37 | return &server{authfunc: authfunc, version: version.New("argocd-agent", "principal")} 38 | } 39 | 40 | func (s *server) Version(ctx context.Context, r *versionapi.VersionRequest) (*versionapi.VersionResponse, error) { 41 | return &versionapi.VersionResponse{Version: s.version.QualifiedVersion()}, nil 42 | } 43 | 44 | func (s *server) AuthFuncOverride(ctx context.Context, fullMethodName string) (context.Context, error) { 45 | _, ok := unauthenticatedMethods[fullMethodName] 46 | if ok { 47 | return ctx, nil 48 | } 49 | if s.authfunc != nil { 50 | return s.authfunc(ctx) 51 | } 52 | return ctx, status.Error(codes.Unauthenticated, "no session") 53 | } 54 | -------------------------------------------------------------------------------- /principal/apis/version/version.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | option go_package = "github.com/argoproj-labs/argocd-agent/pkg/api/grpc/versionapi"; 17 | 18 | package versionapi; 19 | 20 | import "google/api/annotations.proto"; 21 | 22 | message VersionRequest { 23 | } 24 | 25 | message VersionResponse { 26 | string version = 1; 27 | } 28 | 29 | service Version { 30 | rpc Version(VersionRequest) returns (VersionResponse) { 31 | option (google.api.http).get = "/api/v1/version"; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /principal/apis/version/version_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package version 16 | 17 | import ( 18 | "context" 19 | "testing" 20 | 21 | "github.com/argoproj-labs/argocd-agent/pkg/api/grpc/versionapi" 22 | "github.com/stretchr/testify/assert" 23 | ) 24 | 25 | func Test_Version(t *testing.T) { 26 | t.Run("Get version identifier", func(t *testing.T) { 27 | s := NewServer(nil) 28 | r, err := s.Version(context.Background(), &versionapi.VersionRequest{}) 29 | assert.NoError(t, err) 30 | assert.Equal(t, s.version.QualifiedVersion(), r.Version) 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /principal/logging.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package principal 16 | 17 | import ( 18 | "context" 19 | "fmt" 20 | 21 | "github.com/argoproj-labs/argocd-agent/internal/grpcutil" 22 | "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging" 23 | "github.com/sirupsen/logrus" 24 | "google.golang.org/grpc" 25 | ) 26 | 27 | func InterceptorLogger(l logrus.FieldLogger) logging.Logger { 28 | return logging.LoggerFunc(func(_ context.Context, lvl logging.Level, msg string, fields ...any) { 29 | f := make(map[string]any, len(fields)/2) 30 | i := logging.Fields(fields).Iterator() 31 | if i.Next() { 32 | k, v := i.At() 33 | f[k] = v 34 | } 35 | l := l.WithFields(f) 36 | 37 | switch lvl { 38 | case logging.LevelDebug: 39 | l.Debug(msg) 40 | case logging.LevelInfo: 41 | l.Info(msg) 42 | case logging.LevelWarn: 43 | l.Warn(msg) 44 | case logging.LevelError: 45 | l.Error(msg) 46 | default: 47 | panic(fmt.Sprintf("unknown level %v", lvl)) 48 | } 49 | }) 50 | } 51 | 52 | func unaryRequestLogger() grpc.UnaryServerInterceptor { 53 | return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { 54 | log().WithFields(logrus.Fields{ 55 | "method": info.FullMethod, 56 | "client_addr": grpcutil.AddressFromContext(ctx), 57 | }).Debug("Processing unary gRPC request") 58 | return handler(ctx, req) 59 | } 60 | } 61 | 62 | func streamRequestLogger() grpc.StreamServerInterceptor { 63 | return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { 64 | log().WithFields(logrus.Fields{ 65 | "method": info.FullMethod, 66 | "client_addr": grpcutil.AddressFromContext(ss.Context()), 67 | }).Debug("Processing unary gRPC request") 68 | return handler(srv, ss) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /principal/mocks/RecvHook.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v2.35.4. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import ( 6 | eventstreammock "github.com/argoproj-labs/argocd-agent/principal/apis/eventstream/mock" 7 | mock "github.com/stretchr/testify/mock" 8 | ) 9 | 10 | // RecvHook is an autogenerated mock type for the RecvHook type 11 | type RecvHook struct { 12 | mock.Mock 13 | } 14 | 15 | // Execute provides a mock function with given fields: s 16 | func (_m *RecvHook) Execute(s *eventstreammock.MockEventServer) error { 17 | ret := _m.Called(s) 18 | 19 | var r0 error 20 | if rf, ok := ret.Get(0).(func(*eventstreammock.MockEventServer) error); ok { 21 | r0 = rf(s) 22 | } else { 23 | r0 = ret.Error(0) 24 | } 25 | 26 | return r0 27 | } 28 | 29 | // NewRecvHook creates a new instance of RecvHook. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. 30 | // The first argument is typically a *testing.T value. 31 | func NewRecvHook(t interface { 32 | mock.TestingT 33 | Cleanup(func()) 34 | }) *RecvHook { 35 | mock := &RecvHook{} 36 | mock.Mock.Test(t) 37 | 38 | t.Cleanup(func() { mock.AssertExpectations(t) }) 39 | 40 | return mock 41 | } 42 | -------------------------------------------------------------------------------- /principal/mocks/SendHook.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v2.35.4. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import ( 6 | eventstreamapi "github.com/argoproj-labs/argocd-agent/pkg/api/grpc/eventstreamapi" 7 | eventstreammock "github.com/argoproj-labs/argocd-agent/principal/apis/eventstream/mock" 8 | 9 | mock "github.com/stretchr/testify/mock" 10 | ) 11 | 12 | // SendHook is an autogenerated mock type for the SendHook type 13 | type SendHook struct { 14 | mock.Mock 15 | } 16 | 17 | // Execute provides a mock function with given fields: s, sub 18 | func (_m *SendHook) Execute(s *eventstreammock.MockEventServer, sub *eventstreamapi.Event) error { 19 | ret := _m.Called(s, sub) 20 | 21 | var r0 error 22 | if rf, ok := ret.Get(0).(func(*eventstreammock.MockEventServer, *eventstreamapi.Event) error); ok { 23 | r0 = rf(s, sub) 24 | } else { 25 | r0 = ret.Error(0) 26 | } 27 | 28 | return r0 29 | } 30 | 31 | // NewSendHook creates a new instance of SendHook. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. 32 | // The first argument is typically a *testing.T value. 33 | func NewSendHook(t interface { 34 | mock.TestingT 35 | Cleanup(func()) 36 | }) *SendHook { 37 | mock := &SendHook{} 38 | mock.Mock.Test(t) 39 | 40 | t.Cleanup(func() { mock.AssertExpectations(t) }) 41 | 42 | return mock 43 | } 44 | -------------------------------------------------------------------------------- /principal/mocks/ServerOption.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v2.35.4. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import ( 6 | server "github.com/argoproj-labs/argocd-agent/principal" 7 | mock "github.com/stretchr/testify/mock" 8 | ) 9 | 10 | // ServerOption is an autogenerated mock type for the ServerOption type 11 | type ServerOption struct { 12 | mock.Mock 13 | } 14 | 15 | // Execute provides a mock function with given fields: o 16 | func (_m *ServerOption) Execute(o *server.ServerOptions) error { 17 | ret := _m.Called(o) 18 | 19 | var r0 error 20 | if rf, ok := ret.Get(0).(func(*server.ServerOptions) error); ok { 21 | r0 = rf(o) 22 | } else { 23 | r0 = ret.Error(0) 24 | } 25 | 26 | return r0 27 | } 28 | 29 | // NewServerOption creates a new instance of ServerOption. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. 30 | // The first argument is typically a *testing.T value. 31 | func NewServerOption(t interface { 32 | mock.TestingT 33 | Cleanup(func()) 34 | }) *ServerOption { 35 | mock := &ServerOption{} 36 | mock.Mock.Test(t) 37 | 38 | t.Cleanup(func() { mock.AssertExpectations(t) }) 39 | 40 | return mock 41 | } 42 | -------------------------------------------------------------------------------- /principal/resourceproxy/options.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package resourceproxy 16 | 17 | import ( 18 | "crypto/tls" 19 | "regexp" 20 | ) 21 | 22 | // ResourceProxyOption is an option setting callback function 23 | type ResourceProxyOption func(p *ResourceProxy) error 24 | 25 | // WithTLSConfig sets the TLS configuration used for the proxy's listener 26 | func WithTLSConfig(t *tls.Config) ResourceProxyOption { 27 | return func(p *ResourceProxy) error { 28 | p.tlsConfig = t 29 | return nil 30 | } 31 | } 32 | 33 | // WithRequestMatcher adds a request matcher to the proxy. The handler fn will 34 | // be executed when pattern matches on the request URI's path. 35 | func WithRequestMatcher(pattern string, methods []string, fn HandlerFunc) ResourceProxyOption { 36 | return func(p *ResourceProxy) error { 37 | rm, err := matcher(pattern, methods, fn) 38 | if err != nil { 39 | return err 40 | } 41 | p.interceptors = append(p.interceptors, rm) 42 | return nil 43 | } 44 | } 45 | 46 | // matcher creates and returns a new request matcher for the given pattern. 47 | // If the pattern contains submatches, mapping 48 | func matcher(pattern string, methods []string, fn HandlerFunc) (requestMatcher, error) { 49 | matcher, err := regexp.Compile(pattern) 50 | if err != nil { 51 | return requestMatcher{}, err 52 | } 53 | rm := requestMatcher{ 54 | pattern: pattern, 55 | matcher: matcher, 56 | methods: methods, 57 | fn: fn, 58 | } 59 | return rm, nil 60 | } 61 | 62 | func (rp *ResourceProxy) WithRequestMatcher(pattern string, mapping []string, fn HandlerFunc) error { 63 | f := WithRequestMatcher(pattern, mapping, fn) 64 | return f(rp) 65 | } 66 | -------------------------------------------------------------------------------- /principal/resourceproxy/params.go: -------------------------------------------------------------------------------- 1 | package resourceproxy 2 | 3 | // Params is a typed map for storing interceptor parameters. 4 | // Note that the same restrictions apply as for standard maps, i.e. Params 5 | // do not provide thread safety and should not be accessed concurrently. 6 | type Params map[string]string 7 | 8 | // Set sets param key to the given value 9 | func (p Params) Set(key string, value string) { 10 | _, ok := p[key] 11 | if !ok { 12 | p[key] = value 13 | } 14 | } 15 | 16 | // Get retrieves the param with given key. If no such param exists, nil is 17 | // returned. 18 | func (p Params) Get(key string) string { 19 | v, ok := p[key] 20 | if !ok { 21 | return "" 22 | } 23 | return v 24 | } 25 | 26 | // Has returns true if the given param exists 27 | func (p Params) Has(key string) bool { 28 | _, ok := p[key] 29 | return ok 30 | } 31 | 32 | // NewParams returns a new set of Params 33 | func NewParams() Params { 34 | return make(Params) 35 | } 36 | -------------------------------------------------------------------------------- /principal/resourceproxy/params_test.go: -------------------------------------------------------------------------------- 1 | package resourceproxy 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func Test_Params_Set(t *testing.T) { 10 | p := NewParams() 11 | p.Set("some", "value") 12 | assert.Equal(t, "value", p["some"]) 13 | assert.Empty(t, p["value"]) 14 | } 15 | 16 | func Test_Params_Get(t *testing.T) { 17 | p := NewParams() 18 | p.Set("some", "value") 19 | assert.Equal(t, "value", p.Get("some")) 20 | assert.Empty(t, p.Get("value")) 21 | } 22 | 23 | func Test_Params_Has(t *testing.T) { 24 | p := NewParams() 25 | p.Set("some", "value") 26 | assert.True(t, p.Has("some")) 27 | assert.False(t, p.Has("value")) 28 | } 29 | -------------------------------------------------------------------------------- /principal/resourceproxy/tracking_test.go: -------------------------------------------------------------------------------- 1 | package resourceproxy 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func Test_Tracking(t *testing.T) { 11 | rp, err := New("127.0.0.1:0") 12 | require.NoError(t, err) 13 | ch, err := rp.Track("123", "agent") 14 | assert.NotNil(t, ch) 15 | assert.NoError(t, err) 16 | t.Run("Check that request is tracked", func(t *testing.T) { 17 | agent, ch := rp.Tracked("123") 18 | assert.Equal(t, "agent", agent) 19 | assert.NotNil(t, ch) 20 | }) 21 | t.Run("Track existing request", func(t *testing.T) { 22 | ch, err := rp.Track("123", "agent") 23 | assert.Nil(t, ch) 24 | assert.ErrorContains(t, err, "already tracked") 25 | }) 26 | t.Run("Stop tracking request", func(t *testing.T) { 27 | err := rp.StopTracking("123") 28 | require.NoError(t, err) 29 | // Can't stop tracking twice 30 | err = rp.StopTracking("123") 31 | require.Error(t, err) 32 | // It's now untracked 33 | agent, ch := rp.Tracked("123") 34 | assert.Empty(t, agent) 35 | assert.Nil(t, ch) 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /proto/README: -------------------------------------------------------------------------------- 1 | This directory contains proto specifications that we directly vendor into our 2 | tree, because they are not available from our dependencies. 3 | -------------------------------------------------------------------------------- /proto/google/api/README.md: -------------------------------------------------------------------------------- 1 | This folder contains the schema of the configuration model for the API services 2 | platform. 3 | 4 | **Note**: Protos under this directory are in Alpha status, and therefore are 5 | subject to breaking changes. 6 | -------------------------------------------------------------------------------- /proto/google/api/annotations.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | package google.api; 18 | 19 | import "google/api/http.proto"; 20 | import "google/protobuf/descriptor.proto"; 21 | 22 | option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; 23 | option java_multiple_files = true; 24 | option java_outer_classname = "AnnotationsProto"; 25 | option java_package = "com.google.api"; 26 | option objc_class_prefix = "GAPI"; 27 | 28 | extend google.protobuf.MethodOptions { 29 | // See `HttpRule`. 30 | HttpRule http = 72295728; 31 | } 32 | -------------------------------------------------------------------------------- /proto/google/api/apikeys/BUILD.bazel: -------------------------------------------------------------------------------- 1 | # This build file includes a target for the Ruby wrapper library for 2 | # google-cloud-api_keys. 3 | 4 | # This is an API workspace, having public visibility by default makes perfect sense. 5 | package(default_visibility = ["//visibility:public"]) 6 | 7 | # Export yaml configs. 8 | exports_files(glob(["*.yaml"])) 9 | 10 | load( 11 | "@com_google_googleapis_imports//:imports.bzl", 12 | "ruby_cloud_gapic_library", 13 | "ruby_gapic_assembly_pkg", 14 | ) 15 | 16 | # Generates a Ruby wrapper client for apikeys. 17 | # Ruby wrapper clients are versionless, but are generated from source protos 18 | # for a particular service version, v2 in this case. 19 | ruby_cloud_gapic_library( 20 | name = "apikeys_ruby_wrapper", 21 | srcs = ["//google/api/apikeys/v2:apikeys_proto_with_info"], 22 | extra_protoc_parameters = [ 23 | "ruby-cloud-api-id=apikeys.googleapis.com", 24 | "ruby-cloud-api-shortname=apikeys", 25 | "ruby-cloud-gem-name=google-cloud-api_keys", 26 | "ruby-cloud-product-url=https://cloud.google.com/api-keys/", 27 | "ruby-cloud-wrapper-of=v2:0.2", 28 | ], 29 | ruby_cloud_description = "An API key is a simple encrypted string that you can use when calling Google Cloud APIs. The API Keys service manages the API keys associated with developer projects.", 30 | ruby_cloud_title = "API Keys", 31 | transport = "grpc+rest", 32 | ) 33 | 34 | # Open Source package. 35 | ruby_gapic_assembly_pkg( 36 | name = "google-cloud-apikeys-ruby", 37 | deps = [ 38 | ":apikeys_ruby_wrapper", 39 | ], 40 | ) 41 | 42 | -------------------------------------------------------------------------------- /proto/google/api/apikeys/v2/apikeys_grpc_service_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "methodConfig": [{ 3 | "name": [ 4 | { "service": "google.api.apikeys.v2.ApiKeys" } 5 | ], 6 | "timeout": "10s" 7 | }] 8 | } 9 | -------------------------------------------------------------------------------- /proto/google/api/apikeys/v2/apikeys_v2.yaml: -------------------------------------------------------------------------------- 1 | type: google.api.Service 2 | config_version: 3 3 | name: apikeys.googleapis.com 4 | title: API Keys API 5 | 6 | apis: 7 | - name: google.api.apikeys.v2.ApiKeys 8 | - name: google.longrunning.Operations 9 | 10 | documentation: 11 | summary: Manages the API keys associated with developer projects. 12 | 13 | backend: 14 | rules: 15 | - selector: 'google.api.apikeys.v2.ApiKeys.*' 16 | deadline: 120.0 17 | - selector: google.longrunning.Operations.GetOperation 18 | deadline: 120.0 19 | 20 | http: 21 | rules: 22 | - selector: google.longrunning.Operations.GetOperation 23 | get: '/v2/{name=operations/*}' 24 | 25 | authentication: 26 | rules: 27 | - selector: 'google.api.apikeys.v2.ApiKeys.*' 28 | oauth: 29 | canonical_scopes: |- 30 | https://www.googleapis.com/auth/cloud-platform 31 | - selector: google.api.apikeys.v2.ApiKeys.GetKey 32 | oauth: 33 | canonical_scopes: |- 34 | https://www.googleapis.com/auth/cloud-platform, 35 | https://www.googleapis.com/auth/cloud-platform.read-only 36 | - selector: google.api.apikeys.v2.ApiKeys.GetKeyString 37 | oauth: 38 | canonical_scopes: |- 39 | https://www.googleapis.com/auth/cloud-platform, 40 | https://www.googleapis.com/auth/cloud-platform.read-only 41 | - selector: google.api.apikeys.v2.ApiKeys.ListKeys 42 | oauth: 43 | canonical_scopes: |- 44 | https://www.googleapis.com/auth/cloud-platform, 45 | https://www.googleapis.com/auth/cloud-platform.read-only 46 | - selector: google.api.apikeys.v2.ApiKeys.LookupKey 47 | oauth: 48 | canonical_scopes: |- 49 | https://www.googleapis.com/auth/cloud-platform, 50 | https://www.googleapis.com/auth/cloud-platform.read-only 51 | - selector: google.longrunning.Operations.GetOperation 52 | oauth: 53 | canonical_scopes: |- 54 | https://www.googleapis.com/auth/cloud-platform, 55 | https://www.googleapis.com/auth/cloud-platform.read-only 56 | -------------------------------------------------------------------------------- /proto/google/api/control.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | package google.api; 18 | 19 | import "google/api/policy.proto"; 20 | 21 | option go_package = "google.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig"; 22 | option java_multiple_files = true; 23 | option java_outer_classname = "ControlProto"; 24 | option java_package = "com.google.api"; 25 | option objc_class_prefix = "GAPI"; 26 | 27 | // Selects and configures the service controller used by the service. 28 | // 29 | // Example: 30 | // 31 | // control: 32 | // environment: servicecontrol.googleapis.com 33 | message Control { 34 | // The service controller environment to use. If empty, no control plane 35 | // feature (like quota and billing) will be enabled. The recommended value for 36 | // most services is servicecontrol.googleapis.com 37 | string environment = 1; 38 | 39 | // Defines policies applying to the API methods of the service. 40 | repeated MethodPolicy method_policies = 4; 41 | } 42 | -------------------------------------------------------------------------------- /proto/google/api/expr/BUILD.bazel: -------------------------------------------------------------------------------- 1 | exports_files(glob(["*.yaml"])) 2 | -------------------------------------------------------------------------------- /proto/google/api/expr/v1alpha1/explain.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | package google.api.expr.v1alpha1; 18 | 19 | import "google/api/expr/v1alpha1/value.proto"; 20 | 21 | option cc_enable_arenas = true; 22 | option go_package = "google.golang.org/genproto/googleapis/api/expr/v1alpha1;expr"; 23 | option java_multiple_files = true; 24 | option java_outer_classname = "ExplainProto"; 25 | option java_package = "com.google.api.expr.v1alpha1"; 26 | 27 | // Values of intermediate expressions produced when evaluating expression. 28 | // Deprecated, use `EvalState` instead. 29 | message Explain { 30 | option deprecated = true; 31 | 32 | // ID and value index of one step. 33 | message ExprStep { 34 | // ID of corresponding Expr node. 35 | int64 id = 1; 36 | 37 | // Index of the value in the values list. 38 | int32 value_index = 2; 39 | } 40 | 41 | // All of the observed values. 42 | // 43 | // The field value_index is an index in the values list. 44 | // Separating values from steps is needed to remove redundant values. 45 | repeated Value values = 1; 46 | 47 | // List of steps. 48 | // 49 | // Repeated evaluations of the same expression generate new ExprStep 50 | // instances. The order of such ExprStep instances matches the order of 51 | // elements returned by Comprehension.iter_range. 52 | repeated ExprStep expr_steps = 2; 53 | } 54 | -------------------------------------------------------------------------------- /proto/google/api/expr/v1beta1/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@rules_proto//proto:defs.bzl", "proto_library") 2 | 3 | # This is an API workspace, having public visibility by default makes perfect sense. 4 | package(default_visibility = ["//visibility:public"]) 5 | 6 | proto_library( 7 | name = "decl_proto", 8 | srcs = ["decl.proto"], 9 | deps = [ 10 | ":expr_proto", 11 | ], 12 | ) 13 | 14 | proto_library( 15 | name = "eval_proto", 16 | srcs = ["eval.proto"], 17 | deps = [ 18 | ":value_proto", 19 | "//google/rpc:status_proto", 20 | ], 21 | ) 22 | 23 | proto_library( 24 | name = "expr_proto", 25 | srcs = ["expr.proto"], 26 | deps = [ 27 | ":source_proto", 28 | "@com_google_protobuf//:struct_proto", 29 | ], 30 | ) 31 | 32 | proto_library( 33 | name = "source_proto", 34 | srcs = ["source.proto"], 35 | ) 36 | 37 | proto_library( 38 | name = "value_proto", 39 | srcs = ["value.proto"], 40 | deps = [ 41 | "@com_google_protobuf//:any_proto", 42 | "@com_google_protobuf//:struct_proto", 43 | ], 44 | ) 45 | 46 | proto_library( 47 | name = "cel_proto", 48 | deps = [ 49 | ":decl_proto", 50 | ":eval_proto", 51 | ":expr_proto", 52 | ":source_proto", 53 | ":value_proto", 54 | "//google/rpc:status_proto", 55 | "@com_google_protobuf//:any_proto", 56 | "@com_google_protobuf//:struct_proto", 57 | ], 58 | ) 59 | 60 | ############################################################################## 61 | # C++ 62 | ############################################################################## 63 | load( 64 | "@com_google_googleapis_imports//:imports.bzl", 65 | "cc_proto_library", 66 | ) 67 | 68 | cc_proto_library( 69 | name = "decl_cc_proto", 70 | deps = [":decl_proto"], 71 | ) 72 | 73 | cc_proto_library( 74 | name = "eval_cc_proto", 75 | deps = [":eval_proto"], 76 | ) 77 | 78 | cc_proto_library( 79 | name = "expr_cc_proto", 80 | deps = [":expr_proto"], 81 | ) 82 | 83 | cc_proto_library( 84 | name = "source_cc_proto", 85 | deps = [":source_proto"], 86 | ) 87 | 88 | cc_proto_library( 89 | name = "value_cc_proto", 90 | deps = [":value_proto"], 91 | ) 92 | -------------------------------------------------------------------------------- /proto/google/api/expr/v1beta1/source.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | 16 | syntax = "proto3"; 17 | 18 | package google.api.expr.v1beta1; 19 | 20 | option cc_enable_arenas = true; 21 | option go_package = "google.golang.org/genproto/googleapis/api/expr/v1beta1;expr"; 22 | option java_multiple_files = true; 23 | option java_outer_classname = "SourceProto"; 24 | option java_package = "com.google.api.expr.v1beta1"; 25 | 26 | // Source information collected at parse time. 27 | message SourceInfo { 28 | // The location name. All position information attached to an expression is 29 | // relative to this location. 30 | // 31 | // The location could be a file, UI element, or similar. For example, 32 | // `acme/app/AnvilPolicy.cel`. 33 | string location = 2; 34 | 35 | // Monotonically increasing list of character offsets where newlines appear. 36 | // 37 | // The line number of a given position is the index `i` where for a given 38 | // `id` the `line_offsets[i] < id_positions[id] < line_offsets[i+1]`. The 39 | // column may be derivd from `id_positions[id] - line_offsets[i]`. 40 | repeated int32 line_offsets = 3; 41 | 42 | // A map from the parse node id (e.g. `Expr.id`) to the character offset 43 | // within source. 44 | map positions = 4; 45 | } 46 | 47 | // A specific position in source. 48 | message SourcePosition { 49 | // The soucre location name (e.g. file name). 50 | string location = 1; 51 | 52 | // The character offset. 53 | int32 offset = 2; 54 | 55 | // The 1-based index of the starting line in the source text 56 | // where the issue occurs, or 0 if unknown. 57 | int32 line = 3; 58 | 59 | // The 0-based index of the starting position within the line of source text 60 | // where the issue occurs. Only meaningful if line is nonzer.. 61 | int32 column = 4; 62 | } 63 | -------------------------------------------------------------------------------- /proto/google/api/label.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | package google.api; 18 | 19 | option cc_enable_arenas = true; 20 | option go_package = "google.golang.org/genproto/googleapis/api/label;label"; 21 | option java_multiple_files = true; 22 | option java_outer_classname = "LabelProto"; 23 | option java_package = "com.google.api"; 24 | option objc_class_prefix = "GAPI"; 25 | 26 | // A description of a label. 27 | message LabelDescriptor { 28 | // Value types that can be used as label values. 29 | enum ValueType { 30 | // A variable-length string. This is the default. 31 | STRING = 0; 32 | 33 | // Boolean; true or false. 34 | BOOL = 1; 35 | 36 | // A 64-bit signed integer. 37 | INT64 = 2; 38 | } 39 | 40 | // The label key. 41 | string key = 1; 42 | 43 | // The type of data that can be assigned to the label. 44 | ValueType value_type = 2; 45 | 46 | // A human-readable description for the label. 47 | string description = 3; 48 | } 49 | -------------------------------------------------------------------------------- /proto/google/api/log.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | package google.api; 18 | 19 | import "google/api/label.proto"; 20 | 21 | option go_package = "google.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig"; 22 | option java_multiple_files = true; 23 | option java_outer_classname = "LogProto"; 24 | option java_package = "com.google.api"; 25 | option objc_class_prefix = "GAPI"; 26 | 27 | // A description of a log type. Example in YAML format: 28 | // 29 | // - name: library.googleapis.com/activity_history 30 | // description: The history of borrowing and returning library items. 31 | // display_name: Activity 32 | // labels: 33 | // - key: /customer_id 34 | // description: Identifier of a library customer 35 | message LogDescriptor { 36 | // The name of the log. It must be less than 512 characters long and can 37 | // include the following characters: upper- and lower-case alphanumeric 38 | // characters [A-Za-z0-9], and punctuation characters including 39 | // slash, underscore, hyphen, period [/_-.]. 40 | string name = 1; 41 | 42 | // The set of labels that are available to describe a specific log entry. 43 | // Runtime requests that contain labels not specified here are 44 | // considered invalid. 45 | repeated LabelDescriptor labels = 2; 46 | 47 | // A human-readable description of this log. This information appears in 48 | // the documentation and can contain details. 49 | string description = 3; 50 | 51 | // The human-readable name for this log. This information appears on 52 | // the user interface and should be concise. 53 | string display_name = 4; 54 | } 55 | -------------------------------------------------------------------------------- /proto/google/api/serviceconfig.yaml: -------------------------------------------------------------------------------- 1 | type: google.api.Service 2 | config_version: 3 3 | name: serviceconfig.googleapis.com 4 | title: Service Config API 5 | 6 | types: 7 | - name: google.api.ConfigChange 8 | - name: google.api.Distribution 9 | - name: google.api.DocumentationRule 10 | - name: google.api.HttpBody 11 | - name: google.api.LabelDescriptor 12 | - name: google.api.Metric 13 | - name: google.api.MonitoredResource 14 | - name: google.api.MonitoredResourceDescriptor 15 | - name: google.api.MonitoredResourceMetadata 16 | - name: google.api.ResourceDescriptor 17 | - name: google.api.ResourceReference 18 | - name: google.api.RoutingRule 19 | - name: google.api.Service 20 | - name: google.api.Visibility 21 | 22 | enums: 23 | - name: google.api.ErrorReason 24 | - name: google.api.FieldBehavior 25 | 26 | documentation: 27 | summary: Lets you define and config your API service. 28 | -------------------------------------------------------------------------------- /proto/google/api/servicecontrol/BUILD.bazel: -------------------------------------------------------------------------------- 1 | # This build file includes a target for the Ruby wrapper library for 2 | # google-cloud-service_control. 3 | 4 | # This is an API workspace, having public visibility by default makes perfect sense. 5 | package(default_visibility = ["//visibility:public"]) 6 | 7 | # Export yaml configs. 8 | exports_files(glob(["*.yaml"])) 9 | 10 | load( 11 | "@com_google_googleapis_imports//:imports.bzl", 12 | "ruby_cloud_gapic_library", 13 | "ruby_gapic_assembly_pkg", 14 | ) 15 | 16 | # Generates a Ruby wrapper client for servicecontrol. 17 | # Ruby wrapper clients are versionless, but are generated from source protos 18 | # for a particular service version, v1 in this case. 19 | ruby_cloud_gapic_library( 20 | name = "servicecontrol_ruby_wrapper", 21 | srcs = ["//google/api/servicecontrol/v1:servicecontrol_proto_with_info"], 22 | extra_protoc_parameters = [ 23 | "ruby-cloud-gem-name=google-cloud-service_control", 24 | "ruby-cloud-env-prefix=SERVICE_CONTROL", 25 | "ruby-cloud-wrapper-of=v1:0.6", 26 | "ruby-cloud-product-url=https://cloud.google.com/service-infrastructure/docs/overview/", 27 | "ruby-cloud-api-id=servicecontrol.googleapis.com", 28 | "ruby-cloud-api-shortname=servicecontrol", 29 | ], 30 | ruby_cloud_description = "The Service Control API provides control plane functionality to managed services, such as logging, monitoring, and status checks.", 31 | ruby_cloud_title = "Service Control API", 32 | transport = "grpc+rest", 33 | ) 34 | 35 | # Open Source package. 36 | ruby_gapic_assembly_pkg( 37 | name = "google-cloud-servicecontrol-ruby", 38 | deps = [ 39 | ":servicecontrol_ruby_wrapper", 40 | ], 41 | ) 42 | -------------------------------------------------------------------------------- /proto/google/api/servicecontrol/v1/servicecontrol_grpc_service_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "methodConfig": [ 3 | { 4 | "name": [ 5 | { 6 | "service": "google.api.servicecontrol.v1.ServiceController", 7 | "method": "Check" 8 | } 9 | ], 10 | "timeout": "5s", 11 | "retryPolicy": { 12 | "maxAttempts": 5, 13 | "initialBackoff": "1s", 14 | "maxBackoff": "10s", 15 | "backoffMultiplier": 1.3, 16 | "retryableStatusCodes": ["UNAVAILABLE"] 17 | } 18 | }, 19 | { 20 | "name": [ 21 | { 22 | "service": "google.api.servicecontrol.v1.ServiceController", 23 | "method": "Report" 24 | } 25 | ], 26 | "timeout": "16s" 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /proto/google/api/servicecontrol/v2/servicecontrol_grpc_service_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "methodConfig": [ 3 | { 4 | "name": [ 5 | { 6 | "service": "google.api.servicecontrol.v2.ServiceController", 7 | "method": "Check" 8 | } 9 | ], 10 | "timeout": "5s", 11 | "retryPolicy": { 12 | "maxAttempts": 5, 13 | "initialBackoff": "1s", 14 | "maxBackoff": "10s", 15 | "backoffMultiplier": 1.3, 16 | "retryableStatusCodes": ["UNAVAILABLE"] 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /proto/google/api/servicemanagement/BUILD.bazel: -------------------------------------------------------------------------------- 1 | # This build file includes a target for the Ruby wrapper library for 2 | # google-cloud-service_management. 3 | 4 | # This is an API workspace, having public visibility by default makes perfect sense. 5 | package(default_visibility = ["//visibility:public"]) 6 | 7 | # Export yaml configs. 8 | exports_files(glob(["*.yaml"])) 9 | 10 | load( 11 | "@com_google_googleapis_imports//:imports.bzl", 12 | "ruby_cloud_gapic_library", 13 | "ruby_gapic_assembly_pkg", 14 | ) 15 | 16 | # Generates a Ruby wrapper client for servicemanagement. 17 | # Ruby wrapper clients are versionless, but are generated from source protos 18 | # for a particular service version, v1 in this case. 19 | ruby_cloud_gapic_library( 20 | name = "servicemanagement_ruby_wrapper", 21 | srcs = ["//google/api/servicemanagement/v1:servicemanagement_proto_with_info"], 22 | extra_protoc_parameters = [ 23 | "ruby-cloud-gem-name=google-cloud-service_management", 24 | "ruby-cloud-env-prefix=SERVICE_MANAGEMENT", 25 | "ruby-cloud-wrapper-of=v1:0.5", 26 | "ruby-cloud-product-url=https://cloud.google.com/service-infrastructure/docs/overview/", 27 | "ruby-cloud-api-id=servicemanagement.googleapis.com", 28 | "ruby-cloud-api-shortname=servicemanagement", 29 | ], 30 | ruby_cloud_description = "Google Service Management allows service producers to publish their services on Google Cloud Platform so that they can be discovered and used by service consumers.", 31 | ruby_cloud_title = "Service Management", 32 | transport = "grpc+rest", 33 | ) 34 | 35 | # Open Source package. 36 | ruby_gapic_assembly_pkg( 37 | name = "google-cloud-servicemanagement-ruby", 38 | deps = [ 39 | ":servicemanagement_ruby_wrapper", 40 | ], 41 | ) 42 | -------------------------------------------------------------------------------- /proto/google/api/servicemanagement/v1/servicemanagement_gapic.yaml: -------------------------------------------------------------------------------- 1 | type: com.google.api.codegen.ConfigProto 2 | config_schema_version: 2.0.0 3 | # The settings of generated code in a specific language. 4 | language_settings: 5 | java: 6 | package_name: com.google.cloud.api.servicemanagement.v1 7 | -------------------------------------------------------------------------------- /proto/google/api/servicemanagement/v1/servicemanagement_grpc_service_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "methodConfig": [ 3 | { 4 | "name": [ 5 | { 6 | "service": "google.api.servicemanagement.v1.ServiceManager" 7 | } 8 | ], 9 | "timeout": "10s" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /proto/google/api/serviceusage/BUILD.bazel: -------------------------------------------------------------------------------- 1 | # This build file includes a target for the Ruby wrapper library for 2 | # google-cloud-service_usage. 3 | 4 | # This is an API workspace, having public visibility by default makes perfect sense. 5 | package(default_visibility = ["//visibility:public"]) 6 | 7 | # Export yaml configs. 8 | exports_files(glob(["*.yaml"])) 9 | 10 | load( 11 | "@com_google_googleapis_imports//:imports.bzl", 12 | "ruby_cloud_gapic_library", 13 | "ruby_gapic_assembly_pkg", 14 | ) 15 | 16 | # Generates a Ruby wrapper client for serviceusage. 17 | # Ruby wrapper clients are versionless, but are generated from source protos 18 | # for a particular service version, v1 in this case. 19 | ruby_cloud_gapic_library( 20 | name = "serviceusage_ruby_wrapper", 21 | srcs = ["//google/api/serviceusage/v1:serviceusage_proto_with_info"], 22 | extra_protoc_parameters = [ 23 | "ruby-cloud-gem-name=google-cloud-service_usage", 24 | "ruby-cloud-env-prefix=SERVICE_USAGE", 25 | "ruby-cloud-wrapper-of=v1:0.0", 26 | "ruby-cloud-product-url=https://cloud.google.com/service-usage/", 27 | "ruby-cloud-api-id=serviceusage.googleapis.com", 28 | "ruby-cloud-api-shortname=serviceusage", 29 | ], 30 | ruby_cloud_description = "Service Usage is an infrastructure service of Google Cloud that lets you list and manage other APIs and services in your Cloud projects. You can list and manage Google Cloud services and their APIs, as well as services created using Cloud Endpoints.", 31 | ruby_cloud_title = "Service Usage", 32 | ) 33 | 34 | # Open Source package. 35 | ruby_gapic_assembly_pkg( 36 | name = "google-cloud-serviceusage-ruby", 37 | deps = [ 38 | ":serviceusage_ruby_wrapper", 39 | ], 40 | ) 41 | -------------------------------------------------------------------------------- /proto/google/api/serviceusage/v1/serviceusage_grpc_service_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "methodConfig": [{ 3 | "name": [ 4 | { "service": "google.api.serviceusage.v1.ServiceUsage" } 5 | ], 6 | "timeout": "60s" 7 | }] 8 | } 9 | -------------------------------------------------------------------------------- /proto/google/api/serviceusage/v1beta1/serviceusage_grpc_service_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "methodConfig": [{ 3 | "name": [ 4 | { "service": "google.api.serviceusage.v1beta1.ServiceUsage" } 5 | ], 6 | "timeout": "5s" 7 | }] 8 | } 9 | -------------------------------------------------------------------------------- /proto/google/api/source_info.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | package google.api; 18 | 19 | import "google/protobuf/any.proto"; 20 | 21 | option go_package = "google.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig"; 22 | option java_multiple_files = true; 23 | option java_outer_classname = "SourceInfoProto"; 24 | option java_package = "com.google.api"; 25 | option objc_class_prefix = "GAPI"; 26 | 27 | // Source information used to create a Service Config 28 | message SourceInfo { 29 | // All files used during config generation. 30 | repeated google.protobuf.Any source_files = 1; 31 | } 32 | -------------------------------------------------------------------------------- /proto/google/protobuf/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2008 Google Inc. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | Code generated by the Protocol Buffer compiler is owned by the owner 30 | of the input file used when generating it. This code is not 31 | standalone and requires a support library to be linked with it. This 32 | support library is itself covered by the above license. 33 | -------------------------------------------------------------------------------- /proto/google/protobuf/empty.proto: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // https://developers.google.com/protocol-buffers/ 4 | // 5 | // Redistribution and use in source and binary forms, with or without 6 | // modification, are permitted provided that the following conditions are 7 | // met: 8 | // 9 | // * Redistributions of source code must retain the above copyright 10 | // notice, this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above 12 | // copyright notice, this list of conditions and the following disclaimer 13 | // in the documentation and/or other materials provided with the 14 | // distribution. 15 | // * Neither the name of Google Inc. nor the names of its 16 | // contributors may be used to endorse or promote products derived from 17 | // this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | syntax = "proto3"; 32 | 33 | package google.protobuf; 34 | 35 | option go_package = "google.golang.org/protobuf/types/known/emptypb"; 36 | option java_package = "com.google.protobuf"; 37 | option java_outer_classname = "EmptyProto"; 38 | option java_multiple_files = true; 39 | option objc_class_prefix = "GPB"; 40 | option csharp_namespace = "Google.Protobuf.WellKnownTypes"; 41 | option cc_enable_arenas = true; 42 | 43 | // A generic empty message that you can re-use to avoid defining duplicated 44 | // empty messages in your APIs. A typical example is to use it as the request 45 | // or the response type of an API method. For instance: 46 | // 47 | // service Foo { 48 | // rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); 49 | // } 50 | // 51 | message Empty {} 52 | -------------------------------------------------------------------------------- /proto/google/protobuf/source_context.proto: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // https://developers.google.com/protocol-buffers/ 4 | // 5 | // Redistribution and use in source and binary forms, with or without 6 | // modification, are permitted provided that the following conditions are 7 | // met: 8 | // 9 | // * Redistributions of source code must retain the above copyright 10 | // notice, this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above 12 | // copyright notice, this list of conditions and the following disclaimer 13 | // in the documentation and/or other materials provided with the 14 | // distribution. 15 | // * Neither the name of Google Inc. nor the names of its 16 | // contributors may be used to endorse or promote products derived from 17 | // this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | syntax = "proto3"; 32 | 33 | package google.protobuf; 34 | 35 | option java_package = "com.google.protobuf"; 36 | option java_outer_classname = "SourceContextProto"; 37 | option java_multiple_files = true; 38 | option objc_class_prefix = "GPB"; 39 | option csharp_namespace = "Google.Protobuf.WellKnownTypes"; 40 | option go_package = "google.golang.org/protobuf/types/known/sourcecontextpb"; 41 | 42 | // `SourceContext` represents information about the source of a 43 | // protobuf element, like the file in which it is defined. 44 | message SourceContext { 45 | // The path-qualified name of the .proto file that contained the associated 46 | // protobuf element. For example: `"google/protobuf/source_context.proto"`. 47 | string file_name = 1; 48 | } 49 | -------------------------------------------------------------------------------- /test/e2e2/fixture/argosync.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package fixture 16 | 17 | import ( 18 | "context" 19 | 20 | argoapp "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" 21 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 22 | "k8s.io/apimachinery/pkg/types" 23 | ) 24 | 25 | // SyncApplication syncs the named application using the "hook" strategy 26 | func SyncApplication(ctx context.Context, appKey types.NamespacedName, kclient KubeClient) error { 27 | operation := argoapp.Operation{ 28 | Sync: &argoapp.SyncOperation{ 29 | SyncStrategy: &argoapp.SyncStrategy{ 30 | Hook: &argoapp.SyncStrategyHook{}, 31 | }, 32 | }, 33 | InitiatedBy: argoapp.OperationInitiator{ 34 | Username: "e2e", 35 | }, 36 | } 37 | err := SyncApplicationWithOperation(ctx, appKey, operation, kclient) 38 | return err 39 | } 40 | 41 | // SyncApplicationWithOperation syncs the named application using the provided operation 42 | func SyncApplicationWithOperation(ctx context.Context, appKey types.NamespacedName, operation argoapp.Operation, kclient KubeClient) error { 43 | var err error 44 | var app argoapp.Application 45 | 46 | err = kclient.Get(ctx, appKey, &app, metav1.GetOptions{}) 47 | if err != nil { 48 | return err 49 | } 50 | app.Operation = &operation 51 | err = kclient.Update(ctx, &app, metav1.UpdateOptions{}) 52 | return err 53 | } 54 | -------------------------------------------------------------------------------- /test/e2e2/fixture/goreman.go: -------------------------------------------------------------------------------- 1 | package fixture 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "os/exec" 7 | ) 8 | 9 | // StopProcess stops the named process managed by goreman 10 | func StopProcess(processName string) error { 11 | cmd := exec.Command("goreman", "run", "stop", processName) 12 | return cmd.Run() 13 | } 14 | 15 | // StartProcess starts the named process managed by goreman 16 | func StartProcess(processName string) error { 17 | cmd := exec.Command("goreman", "run", "start", processName) 18 | return cmd.Run() 19 | } 20 | 21 | // IsProcessRunning returns whether the named process managed by goreman is 22 | // running 23 | func IsProcessRunning(processName string) bool { 24 | out := &bytes.Buffer{} 25 | cmd := exec.Command("goreman", "run", "status") 26 | cmd.Stdout = out 27 | err := cmd.Run() 28 | if err != nil { 29 | panic(err) 30 | } 31 | sc := bufio.NewScanner(out) 32 | for sc.Scan() { 33 | l := sc.Text() 34 | if len(l) < 2 { 35 | panic("unknown output") 36 | } 37 | switch l[0] { 38 | case '*': 39 | // process running 40 | if l[1:] == processName { 41 | return true 42 | } 43 | case ' ': 44 | // process not running 45 | if l[1:] == processName { 46 | return false 47 | } 48 | default: 49 | panic("unknown output") 50 | } 51 | } 52 | // process not found 53 | return false 54 | } 55 | -------------------------------------------------------------------------------- /test/e2e2/fixture/kubeconfig.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package fixture 16 | 17 | import ( 18 | "os" 19 | 20 | "k8s.io/client-go/rest" 21 | "k8s.io/client-go/tools/clientcmd" 22 | ) 23 | 24 | // GetSystemKubeConfig retrieves the given kube context from system-level 25 | // Kubernetes config (e.g. ~/.kube/config). Use the empty string to retrieve 26 | // the default context. 27 | func GetSystemKubeConfig(kcontext string) (*rest.Config, error) { 28 | 29 | overrides := clientcmd.ConfigOverrides{} 30 | if len(kcontext) > 0 { 31 | overrides.CurrentContext = kcontext 32 | } 33 | 34 | loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() 35 | clientConfig := clientcmd.NewInteractiveDeferredLoadingClientConfig(loadingRules, &overrides, os.Stdin) 36 | 37 | restConfig, err := clientConfig.ClientConfig() 38 | if err != nil { 39 | return nil, err 40 | } 41 | 42 | return restConfig, nil 43 | } 44 | -------------------------------------------------------------------------------- /test/fake/server.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The argocd-agent Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package fake 16 | 17 | func StartFakeServer() {} 18 | -------------------------------------------------------------------------------- /test/proxy/proxy.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | import ( 4 | "crypto/tls" 5 | "crypto/x509" 6 | "fmt" 7 | "log" 8 | "math/big" 9 | "net" 10 | "net/http" 11 | "net/http/httputil" 12 | "path" 13 | "testing" 14 | 15 | "github.com/argoproj-labs/argocd-agent/test/fake/testcerts" 16 | ) 17 | 18 | // Start a reverse proxy that downgrades the incoming HTTP/2 requests to HTTP/1.1 19 | func StartHTTP2DowngradingProxy(t *testing.T, addr string, target string) *http.Server { 20 | tempDir := t.TempDir() 21 | basePath := path.Join(tempDir, "certs") 22 | testcerts.WriteSelfSignedCert(t, "rsa", basePath, x509.Certificate{SerialNumber: big.NewInt(1)}) 23 | 24 | lis, err := net.Listen("tcp", addr) 25 | if err != nil { 26 | log.Fatal(err) 27 | } 28 | 29 | server := &http.Server{ 30 | TLSConfig: &tls.Config{ 31 | InsecureSkipVerify: true, 32 | }, 33 | Handler: downgradeToHTTP1Handler(target), 34 | Addr: lis.Addr().String(), 35 | } 36 | 37 | //nolint:errcheck 38 | go server.ServeTLS(lis, basePath+".crt", basePath+".key") 39 | 40 | return server 41 | } 42 | 43 | func downgradeToHTTP1Handler(target string) http.Handler { 44 | printHeaders := func(header http.Header) { 45 | for name, values := range header { 46 | for _, value := range values { 47 | fmt.Printf("%s: %s\n", name, value) 48 | } 49 | } 50 | } 51 | return &httputil.ReverseProxy{ 52 | Director: func(req *http.Request) { 53 | fmt.Println("Intercepting a request") 54 | req.URL.Host = target 55 | if req.URL.Scheme == "" { 56 | req.URL.Scheme = "https" 57 | } 58 | 59 | // Request is downgraded to HTTP/1.1 60 | req.ProtoMajor, req.ProtoMinor, req.Proto = 1, 1, "HTTP/1.1" 61 | 62 | // Log headers for debugging 63 | fmt.Println("Request Headers:") 64 | printHeaders(req.Header) 65 | }, 66 | Transport: &http.Transport{ 67 | TLSClientConfig: &tls.Config{ 68 | InsecureSkipVerify: true, 69 | }, 70 | ForceAttemptHTTP2: false, 71 | }, 72 | ModifyResponse: func(res *http.Response) error { 73 | // Ensure the response is sent as HTTP/1.1 74 | res.ProtoMajor = 1 75 | res.ProtoMinor = 1 76 | res.Proto = "HTTP/1.1" 77 | 78 | // Log response headers for debugging 79 | fmt.Println("Response Headers:") 80 | printHeaders(res.Header) 81 | 82 | return nil 83 | }, 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /test/testutil/file.go: -------------------------------------------------------------------------------- 1 | package testutil 2 | 3 | import ( 4 | "io" 5 | "os" 6 | ) 7 | 8 | func MustReadFile(path string) []byte { 9 | f, err := os.Open(path) 10 | if err != nil { 11 | panic(err) 12 | } 13 | defer f.Close() 14 | b, err := io.ReadAll(f) 15 | if err != nil { 16 | panic(err) 17 | } 18 | return b 19 | } 20 | --------------------------------------------------------------------------------