Add a version server in the proxy + use it in agent chart (#35150)

This PR adds an embedded [version server](https://goteleport.com/docs/architecture/agent-update-management/#version-server-and-source-of-truth) in the proxy to address: https://github.com/gravitational/cloud/issues/6773

The version server can be configured through `teleport.yaml`:

```yaml
proxy_service:
  enabled: "yes"
  automatic_upgrades_channels:
    stable/cloud:
      forward_url: https://updates.releases.teleport.dev/v1/stable/cloud
    preview/cloud:
      static_version: v12.5.4
```

The forwarded call results are cached for a minute.
This commit is contained in:
Hugo Shaka
2023-12-12 01:03:45 +00:00
committed by GitHub
parent aa25bfb97a
commit 856bb39ea9
32 changed files with 1113 additions and 1804 deletions
-23
View File
@@ -133,29 +133,6 @@ updates:
- "go"
- "no-changelog"
- package-ecosystem: gomod
directory: "/integrations/kube-agent-updater"
schedule:
interval: weekly
day: "sunday"
time: "09:00" # 9am UTC
open-pull-requests-limit: 10
groups:
go:
update-types:
- "minor"
- "patch"
reviewers:
- codingllama
- hugoShaka
- jentfoo
- rosstimothy
- zmb3
labels:
- "dependencies"
- "go"
- "no-changelog"
- package-ecosystem: cargo
directory: "/"
schedule:
+1 -7
View File
@@ -79,7 +79,7 @@ jobs:
- name: Run base difftest
uses: ./.github/actions/difftest
with:
flags: --skip="${{ steps.find_excluded.outputs.FLAKE_SKIP }}" -e "integrations/operator/**/*" -e "tool/tsh/**/*" -e "integration/**/*" -e "build.assets/**/*" -e "lib/auth/webauthncli/**/*" -e "lib/auth/touchid/**/*" -e "api/**/*" -e "integrations/kube-agent-updater/**/*" -e "examples/teleport-usage/**/*" -e "integrations/access/**" -e "integrations/lib/**" -e "integrations/lib/backoff/backoff_test.go" -e "e2e/**/*"
flags: --skip="${{ steps.find_excluded.outputs.FLAKE_SKIP }}" -e "integrations/operator/**/*" -e "tool/tsh/**/*" -e "integration/**/*" -e "build.assets/**/*" -e "lib/auth/webauthncli/**/*" -e "lib/auth/touchid/**/*" -e "api/**/*" -e "examples/teleport-usage/**/*" -e "integrations/access/**" -e "integrations/lib/**" -e "integrations/lib/backoff/backoff_test.go" -e "e2e/**/*"
target: test-go-unit
- name: Run touch-id difftest
@@ -100,12 +100,6 @@ jobs:
flags: --skip="${{ steps.find_excluded.outputs.FLAKE_SKIP }}" --include "api/**/*" --relative "api"
target: test-api
- name: Run kube-agent-updater difftest
uses: ./.github/actions/difftest
with:
flags: --skip="${{ steps.find_excluded.outputs.FLAKE_SKIP }}" --include "integrations/kube-agent-updater/**/*" --relative "integrations/kube-agent-updater"
target: test-kube-agent-updater
- name: Run teleport-usage difftest
uses: ./.github/actions/difftest
with:
-6
View File
@@ -51,12 +51,6 @@ jobs:
version: ${{ env.GOLANGCI_LINT_VERSION }}
args: --build-tags libfido2,piv
skip-cache: true
- name: golangci-lint (kube-agent-updater)
uses: golangci/golangci-lint-action@3a919529898de77ec3da873e3063ca4b10e7f5cc # v3.7.0
with:
version: ${{ env.GOLANGCI_LINT_VERSION }}
working-directory: integrations/kube-agent-updater
skip-cache: true
- name: golangci-lint (assets/backport)
uses: golangci/golangci-lint-action@3a919529898de77ec3da873e3063ca4b10e7f5cc # v3.7.0
with:
@@ -65,7 +65,7 @@ spec:
- "--agent-name={{ .Release.Name }}"
- "--agent-namespace={{ .Release.Namespace }}"
- "--base-image={{ include "teleport-kube-agent.baseImage" . }}"
- "--version-server={{ $updater.versionServer }}"
- "--version-server={{ tpl $updater.versionServer . }}"
- "--version-channel={{ $updater.releaseChannel }}"
{{- if $updater.securityContext }}
securityContext: {{- toYaml $updater.securityContext | nindent 10 }}
@@ -57,6 +57,16 @@ tests:
- contains:
path: spec.template.spec.containers[0].args
content: "--agent-namespace=my-namespace"
- it: defaults the updater version server to the proxy address
set:
proxyAddr: proxy.teleport.example.com:443
roles: "custom"
updater:
enabled: true
asserts:
- contains:
path: spec.template.spec.containers[0].args
content: "--version-server=https://proxy.teleport.example.com:443/v1/webapi/automaticupgrades/channel"
- it: sets the updater version server
values:
- ../.lint/updater.yaml
@@ -160,7 +160,8 @@ updater:
# `updater.versionServer` is the URL of the version server the agent fetches
# the target version from. The complete version endpoint is built by
# concatenating `versionServer` and `releaseChannel`.
versionServer: "https://updates.releases.teleport.dev/v1/"
# This field supports gotemplate.
versionServer: "https://{{ .Values.proxyAddr }}/v1/webapi/automaticupgrades/channel"
# Release channel the agent subscribes to.
releaseChannel: "stable/cloud"
image: public.ecr.aws/gravitational/teleport-kube-agent-updater
+88 -28
View File
@@ -25,7 +25,7 @@ require (
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/sql/armsql v1.2.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/subscription/armsubscription v1.2.0
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.0
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161
github.com/ClickHouse/ch-go v0.61.0
github.com/ClickHouse/clickhouse-go/v2 v2.16.0
github.com/HdrHistogram/hdrhistogram-go v1.1.2
@@ -136,7 +136,7 @@ require (
github.com/prometheus/client_golang v1.17.0
github.com/prometheus/client_model v0.5.0
github.com/prometheus/common v0.45.0
github.com/redis/go-redis/v9 v9.0.2 // replaced
github.com/redis/go-redis/v9 v9.3.0 // replaced
github.com/russellhaering/gosaml2 v0.9.1
github.com/russellhaering/goxmldsig v1.4.0
github.com/sashabaranov/go-openai v1.17.9
@@ -166,7 +166,7 @@ require (
go.opentelemetry.io/otel/trace v1.21.0
go.opentelemetry.io/proto/otlp v1.0.0
golang.org/x/crypto v0.16.0
golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb
golang.org/x/exp v0.0.0-20231006140011-7918f672742d
golang.org/x/mod v0.14.0
golang.org/x/net v0.19.0
golang.org/x/oauth2 v0.15.0
@@ -195,7 +195,7 @@ require (
k8s.io/component-base v0.28.4
k8s.io/klog/v2 v2.110.1
k8s.io/kubectl v0.28.4
sigs.k8s.io/controller-runtime v0.15.3
sigs.k8s.io/controller-runtime v0.16.1
sigs.k8s.io/controller-tools v0.13.0
sigs.k8s.io/yaml v1.4.0
software.sslmate.com/src/go-pkcs12 v0.4.0
@@ -208,6 +208,15 @@ require (
github.com/mailgun/minheap v0.0.0-20170619185613-3dbe6c6bf55f // indirect
)
require (
github.com/Masterminds/sprig/v3 v3.2.3
github.com/distribution/reference v0.5.0
github.com/google/go-containerregistry v0.17.0
github.com/opencontainers/go-digest v1.0.0
github.com/sigstore/cosign/v2 v2.2.1
github.com/sigstore/sigstore v1.7.5
)
require (
cloud.google.com/go v0.110.10 // indirect
cloud.google.com/go/longrunning v0.5.4 // indirect
@@ -216,15 +225,17 @@ require (
github.com/99designs/keyring v1.2.2 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.0 // indirect
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.0 // indirect
github.com/BurntSushi/toml v1.1.0 // indirect
github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c // indirect
github.com/MakeNowJust/heredoc v1.0.0 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.2.0 // indirect
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect
github.com/apache/arrow/go/v12 v12.0.1 // indirect
github.com/apache/thrift v0.16.0 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.9 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.9 // indirect
@@ -239,46 +250,64 @@ require (
github.com/aws/aws-sdk-go-v2/service/sso v1.18.5 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.5 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver v3.5.1+incompatible // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/boombuler/barcode v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/chai2010/gettext-go v1.0.2 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/coreos/pkg v0.0.0-20220810130054-c7d1c02cb6cf // indirect
github.com/crewjam/httperr v0.2.0 // indirect
github.com/danieljoos/wincred v1.1.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/cyberphone/json-canonicalization v0.0.0-20231011164504-785e29786b46 // indirect
github.com/danieljoos/wincred v1.2.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/daviddengcn/go-colortext v1.0.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/digitorus/timestamp v0.0.0-20230902153158-687734543647 // indirect
github.com/dlclark/regexp2 v1.9.0 // indirect
github.com/dmarkham/enumer v1.5.9 // indirect
github.com/docker/distribution v2.8.2+incompatible // indirect
github.com/docker/cli v24.0.7+incompatible // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker v24.0.7+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.0 // indirect
github.com/dvsekhvalnov/jose2go v1.5.0 // indirect
github.com/elastic/elastic-transport-go/v8 v8.3.0 // indirect
github.com/emicklei/go-restful/v3 v3.9.0 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
github.com/fatih/camelcase v1.0.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/form3tech-oss/jwt-go v3.2.5+incompatible // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/fvbommel/sortorder v1.1.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
github.com/go-chi/chi v4.1.2+incompatible // indirect
github.com/go-errors/errors v1.4.2 // indirect
github.com/go-faster/city v1.0.1 // indirect
github.com/go-faster/errors v0.7.0 // indirect
github.com/go-jose/go-jose/v3 v3.0.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-logr/zapr v1.2.4 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/analysis v0.21.4 // indirect
github.com/go-openapi/errors v0.20.4 // indirect
github.com/go-openapi/jsonpointer v0.20.0 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/go-openapi/loads v0.21.2 // indirect
github.com/go-openapi/runtime v0.26.0 // indirect
github.com/go-openapi/spec v0.20.9 // indirect
github.com/go-openapi/strfmt v0.21.7 // indirect
github.com/go-openapi/swag v0.22.4 // indirect
github.com/go-openapi/validate v0.22.1 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.15.5 // indirect
github.com/go-webauthn/x v0.1.5 // indirect
github.com/gobuffalo/flect v1.0.2 // indirect
github.com/goccy/go-json v0.10.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect
github.com/golang-jwt/jwt/v5 v5.2.0 // indirect
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect
@@ -286,9 +315,9 @@ require (
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/certificate-transparency-go v1.1.2 // indirect
github.com/google/certificate-transparency-go v1.1.7 // indirect
github.com/google/flatbuffers v23.1.21+incompatible // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect
github.com/google/go-tpm v0.9.0 // indirect
github.com/google/go-tspi v0.3.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
@@ -299,11 +328,14 @@ require (
github.com/gorilla/mux v1.8.0 // indirect
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0-rc.2.0.20220308023801-e4a6915ea237 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/imdario/mergo v0.3.13 // indirect
github.com/hashicorp/hcl v1.0.1-vault-5 // indirect
github.com/huandu/xstrings v1.3.3 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/in-toto/in-toto-golang v0.9.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgio v1.0.0 // indirect
@@ -315,6 +347,7 @@ require (
github.com/jcmturner/gofork v1.7.6 // indirect
github.com/jcmturner/goidentity/v6 v6.0.1 // indirect
github.com/jcmturner/rpc/v2 v2.0.3 // indirect
github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/josharian/native v1.1.0 // indirect
@@ -327,9 +360,11 @@ require (
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/lib/pq v1.10.4 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/letsencrypt/boulder v0.0.0-20231026200631-000cd05d5491 // indirect
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
github.com/lithammer/dedent v1.1.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
@@ -340,7 +375,10 @@ require (
github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 // indirect
github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/mitchellh/copystructure v1.0.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/mitchellh/reflectwalk v1.0.0 // indirect
github.com/moby/spdystream v0.2.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
@@ -349,12 +387,16 @@ require (
github.com/mtibben/percent v0.2.1 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 // indirect
github.com/nsf/termbox-go v1.1.1 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0-rc5 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/pascaldekloe/name v1.0.1 // indirect
github.com/patrickmn/go-cache v0.0.0-20180815053127-5633e0862627 // indirect
github.com/paulmach/orb v0.10.0 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
github.com/pierrec/lz4/v4 v4.1.18 // indirect
@@ -362,24 +404,41 @@ require (
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pkg/xattr v0.4.9 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/pquerna/cachecontrol v0.1.0 // indirect
github.com/prometheus/procfs v0.11.1 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/rs/zerolog v1.28.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 // indirect
github.com/sagikazarmark/locafero v0.3.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sasha-s/go-deadlock v0.0.0-20180226215254-237a9547c8a5 // indirect
github.com/sassoftware/relic v7.2.1+incompatible // indirect
github.com/secure-systems-lab/go-securesystemslib v0.7.0 // indirect
github.com/segmentio/asm v1.2.0 // indirect
github.com/segmentio/encoding v0.3.5 // indirect
github.com/shabbyrobe/gocovmerge v0.0.0-20190829150210-3e036491d500 // indirect
github.com/shibumi/go-pathspec v1.3.0 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726 // indirect
github.com/siddontang/go-log v0.0.0-20180807004314-8d05993dda07 // indirect
github.com/sigstore/rekor v1.3.3 // indirect
github.com/sigstore/timestamp-authority v1.2.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.10.0 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.17.0 // indirect
github.com/stretchr/objx v0.5.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
github.com/thales-e-security/pool v0.0.2 // indirect
github.com/theupdateframework/go-tuf v0.6.1 // indirect
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
github.com/transparency-dev/merkle v0.0.2 // indirect
github.com/vbatts/tar-split v0.11.5 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.2 // indirect
@@ -396,22 +455,23 @@ require (
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.26.0 // indirect
golang.org/x/tools v0.16.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
gomodules.xyz/jsonpatch/v2 v2.3.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect
gopkg.in/go-jose/go-jose.v2 v2.6.1 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect
k8s.io/component-helpers v0.28.4 // indirect
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
k8s.io/metrics v0.28.4 // indirect
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 // indirect
sigs.k8s.io/kustomize/kustomize/v5 v5.0.4-0.20230601165947-6ce0bf390ce3 // indirect
sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.3.0 // indirect
)
// Update also `ignore` in .github/dependabot.yml.
+358 -483
View File
File diff suppressed because it is too large Load Diff
+165
View File
@@ -0,0 +1,165 @@
/*
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package proxy
import (
"context"
"crypto/tls"
"fmt"
"io"
"net/http"
"net/url"
"path/filepath"
"testing"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/gravitational/teleport/integration/helpers"
"github.com/gravitational/teleport/integrations/kube-agent-updater/pkg/basichttp"
"github.com/gravitational/teleport/integrations/kube-agent-updater/pkg/constants"
"github.com/gravitational/teleport/lib/automaticupgrades"
"github.com/gravitational/teleport/lib/service/servicecfg"
"github.com/gravitational/teleport/lib/utils"
)
func createProxyWithChannels(t *testing.T, channels automaticupgrades.Channels) string {
require.NoError(t, channels.CheckAndSetDefaults())
testDir := t.TempDir()
cfg := helpers.InstanceConfig{
ClusterName: "root.example.com",
HostID: uuid.New().String(),
NodeName: helpers.Loopback,
Log: utils.NewLoggerForTests(),
}
cfg.Listeners = helpers.SingleProxyPortSetup(t, &cfg.Fds)
rc := helpers.NewInstance(t, cfg)
var err error
rcConf := servicecfg.MakeDefaultConfig()
rcConf.DataDir = filepath.Join(testDir, "data")
rcConf.Auth.Enabled = true
rcConf.Proxy.Enabled = true
rcConf.SSH.Enabled = false
rcConf.Proxy.DisableWebInterface = true
rcConf.Version = "v3"
rcConf.Proxy.AutomaticUpgradesChannels = channels
err = rc.CreateEx(t, nil, rcConf)
require.NoError(t, err)
err = rc.Start()
require.NoError(t, err)
t.Cleanup(func() {
assert.NoError(t, rc.StopAll())
})
return cfg.Listeners.Web
}
func TestVersionServer(t *testing.T) {
// Test setup
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
testVersion := "v12.2.6"
testVersionMajorTooHigh := "v99.1.3"
staticChannel := "static/ok"
staticHighChannel := "static/high"
forwardChannel := "forward/ok"
forwardHighChannel := "forward/high"
forwardPath := "/version-server/"
upstreamServer := basichttp.NewServerMock(forwardPath + constants.VersionPath)
upstreamServer.SetResponse(t, http.StatusOK, testVersion)
upstreamHighServer := basichttp.NewServerMock(forwardPath + constants.VersionPath)
upstreamHighServer.SetResponse(t, http.StatusOK, testVersionMajorTooHigh)
channels := automaticupgrades.Channels{
staticChannel: {
StaticVersion: testVersion,
},
staticHighChannel: {
StaticVersion: testVersionMajorTooHigh,
},
forwardChannel: {
ForwardURL: upstreamServer.Srv.URL + forwardPath,
},
forwardHighChannel: {
ForwardURL: upstreamHighServer.Srv.URL + forwardPath,
},
}
proxyAddr := createProxyWithChannels(t, channels)
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
httpClient := http.Client{Transport: tr}
tests := []struct {
name string
channel string
expectedResponse string
}{
{
name: "static version OK",
channel: staticChannel,
expectedResponse: testVersion,
},
{
name: "static version too high",
channel: staticHighChannel,
expectedResponse: constants.NoVersion,
},
{
name: "forward version OK",
channel: forwardChannel,
expectedResponse: testVersion,
},
{
name: "forward version too high",
channel: forwardHighChannel,
expectedResponse: constants.NoVersion,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
channelUrl, err := url.Parse(
fmt.Sprintf("https://%s/v1/webapi/automaticupgrades/channel/%s/version", proxyAddr, tt.channel),
)
require.NoError(t, err)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, channelUrl.String(), nil)
require.NoError(t, err)
res, err := httpClient.Do(req)
require.NoError(t, err)
defer res.Body.Close()
body, err := io.ReadAll(res.Body)
require.NoError(t, err)
require.Equal(t, http.StatusOK, res.StatusCode)
require.Equal(t, tt.expectedResponse, string(body))
})
}
}
@@ -39,6 +39,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/log/zap"
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
kubeversionupdater "github.com/gravitational/teleport/integrations/kube-agent-updater"
"github.com/gravitational/teleport/integrations/kube-agent-updater/pkg/controller"
"github.com/gravitational/teleport/integrations/kube-agent-updater/pkg/img"
"github.com/gravitational/teleport/integrations/kube-agent-updater/pkg/maintenance"
@@ -184,6 +185,8 @@ func main() {
os.Exit(1)
}
ctrl.Log.Info("starting the updater", "version", kubeversionupdater.Version, "url", versionServerURL.String())
if err := mgr.Start(ctx); err != nil {
ctrl.Log.Error(err, "failed to start manager, exiting")
os.Exit(1)
-150
View File
@@ -1,150 +0,0 @@
module github.com/gravitational/teleport/integrations/kube-agent-updater
go 1.21
toolchain go1.21.1
require (
github.com/Masterminds/sprig/v3 v3.2.3
github.com/distribution/reference v0.5.0
github.com/google/go-containerregistry v0.17.0
github.com/gravitational/trace v1.3.1
github.com/jonboulle/clockwork v0.4.0
github.com/opencontainers/go-digest v1.0.0
github.com/sigstore/cosign/v2 v2.2.2
github.com/sigstore/sigstore v1.7.6
github.com/stretchr/testify v1.8.4
golang.org/x/exp v0.0.0-20231108232855-2478ac86f678
golang.org/x/mod v0.14.0
k8s.io/api v0.28.4
k8s.io/apimachinery v0.28.4
sigs.k8s.io/controller-runtime v0.16.1
)
require (
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.2.0 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver v3.5.1+incompatible // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
github.com/cyberphone/json-canonicalization v0.0.0-20231011164504-785e29786b46 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 // indirect
github.com/digitorus/timestamp v0.0.0-20230902153158-687734543647 // indirect
github.com/docker/cli v24.0.7+incompatible // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker v24.0.7+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.0 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-chi/chi v4.1.2+incompatible // indirect
github.com/go-logr/logr v1.3.0 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-logr/zapr v1.2.4 // indirect
github.com/go-openapi/analysis v0.21.4 // indirect
github.com/go-openapi/errors v0.20.4 // indirect
github.com/go-openapi/jsonpointer v0.20.0 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/loads v0.21.2 // indirect
github.com/go-openapi/runtime v0.26.0 // indirect
github.com/go-openapi/spec v0.20.11 // indirect
github.com/go-openapi/strfmt v0.21.8 // indirect
github.com/go-openapi/swag v0.22.4 // indirect
github.com/go-openapi/validate v0.22.3 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/certificate-transparency-go v1.1.7 // indirect
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.4.0 // indirect
github.com/hashicorp/hcl v1.0.1-vault-5 // indirect
github.com/huandu/xstrings v1.3.3 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/in-toto/in-toto-golang v0.9.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.2 // indirect
github.com/letsencrypt/boulder v0.0.0-20231026200631-000cd05d5491 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/opencontainers/image-spec v1.1.0-rc5 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.17.0 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.45.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/sagikazarmark/locafero v0.3.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sassoftware/relic v7.2.1+incompatible // indirect
github.com/secure-systems-lab/go-securesystemslib v0.7.0 // indirect
github.com/shibumi/go-pathspec v1.3.0 // indirect
github.com/shopspring/decimal v1.2.0 // indirect
github.com/sigstore/rekor v1.3.4 // indirect
github.com/sigstore/timestamp-authority v1.2.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.10.0 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/cobra v1.8.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.17.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
github.com/theupdateframework/go-tuf v0.7.0 // indirect
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
github.com/transparency-dev/merkle v0.0.2 // indirect
github.com/vbatts/tar-split v0.11.5 // indirect
go.mongodb.org/mongo-driver v1.12.1 // indirect
go.opentelemetry.io/otel v1.21.0 // indirect
go.opentelemetry.io/otel/metric v1.21.0 // indirect
go.opentelemetry.io/otel/trace v1.21.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.26.0 // indirect
golang.org/x/crypto v0.16.0 // indirect
golang.org/x/net v0.19.0 // indirect
golang.org/x/oauth2 v0.15.0 // indirect
golang.org/x/sync v0.5.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/term v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/go-jose/go-jose.v2 v2.6.1 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiextensions-apiserver v0.28.0 // indirect
k8s.io/client-go v0.28.3 // indirect
k8s.io/component-base v0.28.1 // indirect
k8s.io/klog/v2 v2.100.1 // indirect
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.3.0 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)
File diff suppressed because it is too large Load Diff
@@ -31,4 +31,8 @@ const (
VersionPath = "version"
HTTPTimeout = 10 * time.Second
CacheDuration = time.Minute
// NoVersion is returned by the version endpoint when there is no valid target version.
// This can be caused by the target version being incompatible with the cluster version.
NoVersion = "none"
)
@@ -29,6 +29,8 @@ import (
ctrl "sigs.k8s.io/controller-runtime"
kclient "sigs.k8s.io/controller-runtime/pkg/client"
ctrllog "sigs.k8s.io/controller-runtime/pkg/log"
"github.com/gravitational/teleport/integrations/kube-agent-updater/pkg/version"
)
// DeploymentVersionUpdater Reconciles a podSpec by changing its image
@@ -76,7 +78,7 @@ func (r *DeploymentVersionUpdater) Reconcile(ctx context.Context, req ctrl.Reque
image, err := r.GetVersion(ctx, &obj, currentVersion)
var (
noNewVersionErr *NoNewVersionError
noNewVersionErr *version.NoNewVersionError
maintenanceErr *MaintenanceNotTriggeredError
)
switch {
@@ -18,10 +18,6 @@
package controller
import (
"fmt"
)
// MaintenanceNotTriggeredError indicates that no trigger returned true and the controller did not reconcile.
type MaintenanceNotTriggeredError struct {
Message string `json:"message"`
@@ -34,18 +30,3 @@ func (e *MaintenanceNotTriggeredError) Error() string {
}
return "maintenance not triggered"
}
// NoNewVersionError indicates that no new version was found and the controller did not reconcile.
type NoNewVersionError struct {
Message string `json:"message"`
CurrentVersion string `json:"currentVersion"`
NextVersion string `json:"nextVersion"`
}
// Error returns log friendly description of an error
func (e *NoNewVersionError) Error() string {
if e.Message != "" {
return e.Message
}
return fmt.Sprintf("no new version (current: %q, next: %q)", e.CurrentVersion, e.NextVersion)
}
@@ -34,6 +34,7 @@ import (
ctrllog "sigs.k8s.io/controller-runtime/pkg/log"
"github.com/gravitational/teleport/integrations/kube-agent-updater/pkg/podutils"
"github.com/gravitational/teleport/integrations/kube-agent-updater/pkg/version"
)
type StatefulSetVersionUpdater struct {
@@ -101,7 +102,7 @@ func (r *StatefulSetVersionUpdater) Reconcile(ctx context.Context, req ctrl.Requ
image, err := r.GetVersion(ctx, &obj, currentVersion)
var (
noNewVersionErr *NoNewVersionError
noNewVersionErr *version.NoNewVersionError
maintenanceErr *MaintenanceNotTriggeredError
)
switch {
@@ -63,7 +63,7 @@ func (r *VersionUpdater) GetVersion(ctx context.Context, obj client.Object, curr
log.Info("New version candidate", "nextVersion", nextVersion)
if !version.ValidVersionChange(ctx, currentVersion, nextVersion) {
return nil, &NoNewVersionError{CurrentVersion: currentVersion, NextVersion: nextVersion}
return nil, &version.NoNewVersionError{CurrentVersion: currentVersion, NextVersion: nextVersion}
}
log.Info("Version change is valid, building img candidate")
@@ -44,8 +44,8 @@ const (
)
var (
alwaysTrigger = maintenance.NewMaintenanceTriggerMock("always trigger", true)
neverTrigger = maintenance.NewMaintenanceTriggerMock("never trigger", false)
alwaysTrigger = maintenance.NewMaintenanceStaticTrigger("always trigger", true)
neverTrigger = maintenance.NewMaintenanceStaticTrigger("never trigger", false)
alwaysValid = img.NewImageValidatorMock(
"always",
true,
@@ -82,7 +82,7 @@ func Test_VersionUpdater_GetVersion(t *testing.T) {
releaseRegistry: defaultTestRegistry,
releasePath: defaultTestPath,
currentVersion: versionMid,
versionGetter: version.NewGetterMock(versionHigh, nil),
versionGetter: version.NewStaticGetter(versionHigh, nil),
maintenanceTriggers: []maintenance.Trigger{alwaysTrigger},
imageCheckers: []img.Validator{alwaysValid},
assertErr: require.NoError,
@@ -93,7 +93,7 @@ func Test_VersionUpdater_GetVersion(t *testing.T) {
releaseRegistry: defaultTestRegistry,
releasePath: defaultTestPath,
currentVersion: "",
versionGetter: version.NewGetterMock(versionHigh, nil),
versionGetter: version.NewStaticGetter(versionHigh, nil),
maintenanceTriggers: []maintenance.Trigger{alwaysTrigger},
imageCheckers: []img.Validator{alwaysValid},
assertErr: require.NoError,
@@ -104,10 +104,21 @@ func Test_VersionUpdater_GetVersion(t *testing.T) {
releaseRegistry: defaultTestRegistry,
releasePath: defaultTestPath,
currentVersion: versionMid,
versionGetter: version.NewGetterMock(versionMid, nil),
versionGetter: version.NewStaticGetter(versionMid, nil),
maintenanceTriggers: []maintenance.Trigger{alwaysTrigger},
imageCheckers: []img.Validator{alwaysValid},
assertErr: errorIsType(&NoNewVersionError{}),
assertErr: errorIsType(&version.NoNewVersionError{}),
expectedImage: "",
},
{
name: "no version",
releaseRegistry: defaultTestRegistry,
releasePath: defaultTestPath,
currentVersion: versionMid,
versionGetter: version.NewStaticGetter("", &version.NoNewVersionError{Message: "version server did not advertise a version"}),
maintenanceTriggers: []maintenance.Trigger{alwaysTrigger},
imageCheckers: []img.Validator{alwaysValid},
assertErr: errorIsType(&version.NoNewVersionError{}),
expectedImage: "",
},
{
@@ -115,7 +126,7 @@ func Test_VersionUpdater_GetVersion(t *testing.T) {
releaseRegistry: defaultTestRegistry,
releasePath: defaultTestPath,
currentVersion: versionMid,
versionGetter: version.NewGetterMock(versionHigh, nil),
versionGetter: version.NewStaticGetter(versionHigh, nil),
maintenanceTriggers: []maintenance.Trigger{neverTrigger},
imageCheckers: []img.Validator{alwaysValid},
assertErr: errorIsType(&MaintenanceNotTriggeredError{}),
@@ -126,7 +137,7 @@ func Test_VersionUpdater_GetVersion(t *testing.T) {
releaseRegistry: defaultTestRegistry,
releasePath: defaultTestPath,
currentVersion: versionMid,
versionGetter: version.NewGetterMock(versionHigh, nil),
versionGetter: version.NewStaticGetter(versionHigh, nil),
maintenanceTriggers: []maintenance.Trigger{alwaysTrigger},
imageCheckers: []img.Validator{neverValid},
assertErr: errorIsType(&trace.TrustError{}),
@@ -137,7 +148,7 @@ func Test_VersionUpdater_GetVersion(t *testing.T) {
releaseRegistry: defaultTestRegistry,
releasePath: defaultTestPath,
currentVersion: versionMid,
versionGetter: version.NewGetterMock("", &trace.ConnectionProblemError{}),
versionGetter: version.NewStaticGetter("", &trace.ConnectionProblemError{}),
maintenanceTriggers: []maintenance.Trigger{alwaysTrigger},
imageCheckers: []img.Validator{neverValid},
assertErr: errorIsType(&trace.ConnectionProblemError{}),
@@ -161,7 +172,7 @@ func Test_VersionUpdater_GetVersion(t *testing.T) {
baseImage: baseImage,
}
// We need a dummy Kubernetes object, it is not used by the TriggerMock
// We need a dummy Kubernetes object, it is not used by the StaticTrigger
obj := &core.Pod{}
// Doing the test
@@ -24,33 +24,33 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
)
// TriggerMock is a fake Trigger that return a static answer. This is used
// StaticTrigger is a fake Trigger that return a static answer. This is used
// for testing purposes and is inherently disruptive.
type TriggerMock struct {
type StaticTrigger struct {
name string
canStart bool
}
// Name returns the TriggerMock name.
func (m TriggerMock) Name() string {
// Name returns the StaticTrigger name.
func (m StaticTrigger) Name() string {
return m.name
}
// CanStart returns the statically defined maintenance approval result.
func (m TriggerMock) CanStart(_ context.Context, _ client.Object) (bool, error) {
func (m StaticTrigger) CanStart(_ context.Context, _ client.Object) (bool, error) {
return m.canStart, nil
}
// Default returns the default behavior if the trigger fails. This cannot
// happen for a TriggerMock and is here solely to implement the Trigger
// happen for a StaticTrigger and is here solely to implement the Trigger
// interface.
func (m TriggerMock) Default() bool {
func (m StaticTrigger) Default() bool {
return m.canStart
}
// NewMaintenanceTriggerMock creates a TriggerMock
func NewMaintenanceTriggerMock(name string, canStart bool) Trigger {
return TriggerMock{
// NewMaintenanceStaticTrigger creates a StaticTrigger
func NewMaintenanceStaticTrigger(name string, canStart bool) Trigger {
return StaticTrigger{
name: name,
canStart: canStart,
}
@@ -49,8 +49,12 @@ func (b *basicHTTPVersionClient) Get(ctx context.Context) (string, error) {
if err != nil {
return "", trace.Wrap(err)
}
response := string(body)
if response == constants.NoVersion {
return "", &NoNewVersionError{Message: "version server did not advertise a version"}
}
// We trim spaces because the value might end with one or many newlines
version, err := EnsureSemver(strings.TrimSpace(string(body)))
version, err := EnsureSemver(strings.TrimSpace(response))
return version, trace.Wrap(err)
}
@@ -0,0 +1,36 @@
/*
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package version
import "fmt"
// NoNewVersionError indicates that no new version was found and the controller did not reconcile.
type NoNewVersionError struct {
Message string `json:"message"`
CurrentVersion string `json:"currentVersion"`
NextVersion string `json:"nextVersion"`
}
// Error returns log friendly description of an error
func (e *NoNewVersionError) Error() string {
if e.Message != "" {
return e.Message
}
return fmt.Sprintf("no new version (current: %q, next: %q)", e.CurrentVersion, e.NextVersion)
}
@@ -23,25 +23,25 @@ import (
"strings"
)
// GetterMock is a fake version.Getter that return a static answer. This is used
// StaticGetter is a fake version.Getter that return a static answer. This is used
// for testing purposes.
type GetterMock struct {
type StaticGetter struct {
version string
err error
}
// GetVersion returns the statically defined version.
func (v GetterMock) GetVersion(_ context.Context) (string, error) {
func (v StaticGetter) GetVersion(_ context.Context) (string, error) {
return v.version, v.err
}
// NewGetterMock creates a GetterMock
func NewGetterMock(version string, err error) Getter {
// NewStaticGetter creates a StaticGetter
func NewStaticGetter(version string, err error) Getter {
semVersion := version
if semVersion != "" && !strings.HasPrefix(semVersion, "v") {
semVersion = "v" + version
}
return GetterMock{
return StaticGetter{
version: semVersion,
err: err,
}
@@ -40,6 +40,7 @@ import (
kclient "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/envtest"
"sigs.k8s.io/controller-runtime/pkg/manager"
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
"github.com/gravitational/teleport/api/client"
"github.com/gravitational/teleport/api/types"
@@ -181,8 +182,8 @@ func (s *TestSetup) StartKubernetesOperator(t *testing.T) {
}
k8sManager, err := ctrl.NewManager(s.K8sRestConfig, ctrl.Options{
Scheme: scheme,
MetricsBindAddress: "0",
Scheme: scheme,
Metrics: metricsserver.Options{BindAddress: "0"},
})
require.NoError(t, err)
+8 -3
View File
@@ -35,6 +35,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/cache"
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
resourcesv1 "github.com/gravitational/teleport/integrations/operator/apis/resources/v1"
resourcesv2 "github.com/gravitational/teleport/integrations/operator/apis/resources/v2"
@@ -100,8 +101,10 @@ func main() {
}
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
MetricsBindAddress: config.metricsAddr,
Scheme: scheme,
Metrics: metricsserver.Options{
BindAddress: config.metricsAddr,
},
HealthProbeBindAddress: config.probeAddr,
LeaderElection: true,
LeaderElectionID: config.leaderElectionID,
@@ -109,7 +112,9 @@ func main() {
PprofBindAddress: config.pprofAddr,
Cache: cache.Options{
SyncPeriod: &config.syncPeriod,
Namespaces: []string{config.namespace},
DefaultNamespaces: map[string]cache.Config{
config.namespace: {},
},
},
})
if err != nil {
+99
View File
@@ -0,0 +1,99 @@
/*
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package automaticupgrades
import (
"context"
"net/url"
"github.com/gravitational/trace"
"github.com/gravitational/teleport/integrations/kube-agent-updater/pkg/maintenance"
"github.com/gravitational/teleport/integrations/kube-agent-updater/pkg/version"
)
// Channels is a map of Channel objects.
type Channels map[string]*Channel
// CheckAndSetDefaults checks that every Channel is valid and initializes them.
func (c Channels) CheckAndSetDefaults() error {
var errs []error
var err error
for name, channel := range c {
// Wrapping is not mandatory here, but it adds the channel name in the
// error, which makes troubleshooting easier.
err = trace.Wrap(channel.CheckAndSetDefaults(), "failed to create channel %s", name)
if err != nil {
errs = append(errs, err)
}
}
return trace.NewAggregate(errs...)
}
// Channel describes an automatic update channel configuration.
// It can be configured to serve a static version, or forward version requests
// to an upstream version server. Forwarded results are cached for 1 minute.
type Channel struct {
// ForwardURL is the URL of the upstream version server providing the channel version/criticality.
ForwardURL string `yaml:"forward_url,omitempty"`
// StaticVersion is a static version the channel must serve. With or without the leading "v".
StaticVersion string `yaml:"static_version,omitempty"`
// Critical is whether the static version channel should be marked as a critical update.
Critical bool `yaml:"critical"`
// versionGetter gets the version of the channel. It is populated by CheckAndSetDefaults.
versionGetter version.Getter
// criticalTrigger gets the criticality of the channel. It is populated by CheckAndSetDefaults.
criticalTrigger maintenance.Trigger
}
// CheckAndSetDefaults checks that the Channel configuration is valid and inits
// the version getter and maintenance trigger of the Channel based on its
// configuration. This function must be called before using the channel.
func (c *Channel) CheckAndSetDefaults() error {
switch {
case c.ForwardURL != "" && (c.StaticVersion != "" || c.Critical):
return trace.BadParameter("cannot set both ForwardURL and (StaticVersion or Critical)")
case c.ForwardURL != "":
baseURL, err := url.Parse(c.ForwardURL)
if err != nil {
return trace.Wrap(err)
}
c.versionGetter = version.NewBasicHTTPVersionGetter(baseURL)
c.criticalTrigger = maintenance.NewBasicHTTPMaintenanceTrigger("remote", baseURL)
case c.StaticVersion != "":
c.versionGetter = version.NewStaticGetter(c.StaticVersion, nil)
c.criticalTrigger = maintenance.NewMaintenanceStaticTrigger("remote", c.Critical)
default:
return trace.BadParameter("either ForwardURL or StaticVersion must be set")
}
return nil
}
// GetVersion returns the current version of the channel. If io is involved,
// this function implements cache and is safe to call frequently.
func (c *Channel) GetVersion(ctx context.Context) (string, error) {
return c.versionGetter.GetVersion(ctx)
}
// GetCritical returns the current criticality of the channel. If io is involved,
// this function implements cache and is safe to call frequently.
func (c *Channel) GetCritical(ctx context.Context) (bool, error) {
return c.criticalTrigger.CanStart(ctx, nil)
}
+107
View File
@@ -0,0 +1,107 @@
/*
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package automaticupgrades
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/gravitational/teleport/integrations/kube-agent-updater/pkg/maintenance"
"github.com/gravitational/teleport/integrations/kube-agent-updater/pkg/version"
)
const testVersion = "v1.2.3"
func Test_Channels_CheckAndSetDefaults(t *testing.T) {
t.Run("no channels", func(t *testing.T) {
c := Channels{}
require.NoError(t, c.CheckAndSetDefaults())
})
t.Run("single channel", func(t *testing.T) {
channel := &Channel{StaticVersion: testVersion}
c := Channels{"foo": channel}
require.NoError(t, c.CheckAndSetDefaults())
require.NotNil(t, channel.versionGetter)
require.NotNil(t, channel.criticalTrigger)
})
t.Run("many channels", func(t *testing.T) {
channel1 := &Channel{StaticVersion: testVersion}
channel2 := &Channel{StaticVersion: testVersion}
channel3 := &Channel{StaticVersion: testVersion}
c := Channels{"foo": channel1, "bar": channel2, "baz": channel3}
require.NoError(t, c.CheckAndSetDefaults())
require.NotNil(t, channel1.versionGetter)
require.NotNil(t, channel1.criticalTrigger)
require.NotNil(t, channel2.versionGetter)
require.NotNil(t, channel2.criticalTrigger)
require.NotNil(t, channel3.versionGetter)
require.NotNil(t, channel3.criticalTrigger)
})
}
func Test_Channel_CheckAndSetDefaults(t *testing.T) {
tests := []struct {
name string
channel Channel
assertError require.ErrorAssertionFunc
expectedVersionGetterType interface{}
expectedCriticalTriggerType interface{}
}{
{
name: "empty (invalid)",
channel: Channel{},
assertError: require.Error,
},
{
name: "forward URL (valid)",
channel: Channel{
ForwardURL: stableCloudVersionBaseURL,
},
assertError: require.NoError,
expectedVersionGetterType: version.BasicHTTPVersionGetter{},
expectedCriticalTriggerType: maintenance.BasicHTTPMaintenanceTrigger{},
},
{
name: "static version (valid)",
channel: Channel{
StaticVersion: testVersion,
},
assertError: require.NoError,
expectedVersionGetterType: version.StaticGetter{},
expectedCriticalTriggerType: maintenance.StaticTrigger{},
},
{
name: "all set (invalid)",
channel: Channel{
ForwardURL: stableCloudVersionBaseURL,
StaticVersion: testVersion,
},
assertError: require.Error,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.assertError(t, tt.channel.CheckAndSetDefaults())
require.IsType(t, tt.expectedVersionGetterType, tt.channel.versionGetter)
require.IsType(t, tt.expectedCriticalTriggerType, tt.channel.criticalTrigger)
})
}
}
+7
View File
@@ -1111,6 +1111,13 @@ func applyProxyConfig(fc *FileConfig, cfg *servicecfg.Config) error {
cfg.Proxy.MySQLServerVersion = fc.Proxy.MySQLServerVersion
}
if fc.Proxy.AutomaticUpgradesChannels != nil {
cfg.Proxy.AutomaticUpgradesChannels = fc.Proxy.AutomaticUpgradesChannels
if err := cfg.Proxy.AutomaticUpgradesChannels.CheckAndSetDefaults(); err != nil {
return trace.Wrap(err, "failed to set up automatic update channels")
}
}
// This is the legacy format. Continue to support it forever, but ideally
// users now use the list format below.
if fc.Proxy.KeyFile != "" || fc.Proxy.CertFile != "" {
+6
View File
@@ -46,6 +46,7 @@ import (
apiutils "github.com/gravitational/teleport/api/utils"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/api/utils/tlsutils"
"github.com/gravitational/teleport/lib/automaticupgrades"
"github.com/gravitational/teleport/lib/backend"
"github.com/gravitational/teleport/lib/client"
"github.com/gravitational/teleport/lib/defaults"
@@ -2003,6 +2004,11 @@ type Proxy struct {
// the "X-Forwarded-For" headers for web APIs received from layer 7 load
// balancers or reverse proxies.
TrustXForwardedFor types.Bool `yaml:"trust_x_forwarded_for,omitempty"`
// AutomaticUpgradesChannels is a map of all version channels used by the
// proxy built-in version server to retrieve target versions. This is part
// of the automatic upgrades.
AutomaticUpgradesChannels automaticupgrades.Channels `yaml:"automatic_upgrades_channels,omitempty"`
}
// UIConfig provides config options for the web UI served by the proxy service.
+6 -5
View File
@@ -4051,11 +4051,12 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error {
ctx, err := controller(ctx, sctx, login, localAddr, remoteAddr)
return ctx, trace.Wrap(err)
}),
PROXYSigner: proxySigner,
OpenAIConfig: cfg.Testing.OpenAIConfig,
NodeWatcher: nodeWatcher,
AccessGraphAddr: accessGraphAddr,
TracerProvider: process.TracingProvider,
PROXYSigner: proxySigner,
OpenAIConfig: cfg.Testing.OpenAIConfig,
NodeWatcher: nodeWatcher,
AccessGraphAddr: accessGraphAddr,
TracerProvider: process.TracingProvider,
AutomaticUpgradesChannels: cfg.Proxy.AutomaticUpgradesChannels,
}
webHandler, err := web.NewHandler(webConfig)
if err != nil {
+6
View File
@@ -28,6 +28,7 @@ import (
"github.com/gravitational/trace"
"github.com/gravitational/teleport/api/client/webclient"
"github.com/gravitational/teleport/lib/automaticupgrades"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/limiter"
"github.com/gravitational/teleport/lib/multiplexer"
@@ -156,6 +157,11 @@ type ProxyConfig struct {
// as a label and used by reverse tunnel agents in proxy peering mode. Zero
// is a valid generation.
ProxyGroupGeneration uint64
// AutomaticUpgradesChannels is a map of all version channels used by the
// proxy built-in version server to retrieve target versions. This is part
// of the automatic upgrades.
AutomaticUpgradesChannels automaticupgrades.Channels
}
// WebPublicAddr returns the address for the web endpoint on this proxy that
+9
View File
@@ -293,6 +293,11 @@ type Config struct {
// AccessGraphAddr is the address of the Access Graph service GRPC API
AccessGraphAddr utils.NetAddr
// AutomaticUpgradesChannels is a map of all version channels used by the
// proxy built-in version server to retrieve target versions. This is part
// of the automatic upgrades.
AutomaticUpgradesChannels automaticupgrades.Channels
}
// SetDefaults ensures proper default values are set if
@@ -893,6 +898,10 @@ func (h *Handler) bindDefaultEndpoints() {
// Returns logins included in the Connect My Computer role of the user.
// Returns an empty list of logins if the user does not have a Connect My Computer role assigned.
h.GET("/webapi/connectmycomputer/logins", h.WithAuth(h.connectMyComputerLoginsList))
// Implements the agent version server.
// Channel can contain "/", hence the use of a catch-all parameter
h.GET("/webapi/automaticupgrades/channel/*request", h.WithUnauthenticatedHighLimiter(h.automaticUpgrades))
}
// GetProxyClient returns authenticated auth server client
+146
View File
@@ -0,0 +1,146 @@
/*
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package web
import (
"context"
"net/http"
"strconv"
"strings"
"time"
"github.com/gravitational/trace"
"github.com/julienschmidt/httprouter"
"golang.org/x/mod/semver"
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/integrations/kube-agent-updater/pkg/constants"
versionlib "github.com/gravitational/teleport/integrations/kube-agent-updater/pkg/version"
"github.com/gravitational/teleport/lib/automaticupgrades"
)
const defaultChannelTimeout = 5 * time.Second
// automaticUpgrades implements a version server in the Teleport Proxy.
// It is configured through the Teleport Proxy configuration and tells agent updaters
// which version they should install.
func (h *Handler) automaticUpgrades(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) {
if h.cfg.AutomaticUpgradesChannels == nil {
return nil, trace.AccessDenied("This proxy is not configured to serve automatic upgrades channels.")
}
// The request format is "<channel name>/{version,critical}"
// As <channel name> might contain "/" we have to split, pop the last part
// and re-construct the channel name.
channelAndType := p.ByName("request")
reqParts := strings.Split(strings.Trim(channelAndType, "/"), "/")
if len(reqParts) < 2 {
return nil, trace.BadParameter("path format should be /webapi/automaticupgrades/channel/<channel>/{version,critical}")
}
requestType := reqParts[len(reqParts)-1]
channelName := strings.Join(reqParts[:len(reqParts)-1], "/")
if channelName == "" {
return nil, trace.BadParameter("a channel name is required")
}
// We check if the channel is configured
channel, ok := h.cfg.AutomaticUpgradesChannels[channelName]
if !ok {
return nil, trace.NotFound("channel %s not found", channelName)
}
// Finally, we treat the request based on its type
switch requestType {
case "version":
h.log.Debugf("Agent requesting version for channel %s", channelName)
return h.automaticUpgradesVersion(w, r, channel)
case "critical":
h.log.Debugf("Agent requesting criticality for channel %s", channelName)
return h.automaticUpgradesCritical(w, r, channel)
default:
return nil, trace.BadParameter("requestType path must end with 'version' or 'critical'")
}
}
// automaticUpgradesVersion handles version requests from upgraders
func (h *Handler) automaticUpgradesVersion(w http.ResponseWriter, r *http.Request, channel *automaticupgrades.Channel) (interface{}, error) {
ctx, cancel := context.WithTimeout(r.Context(), defaultChannelTimeout)
defer cancel()
targetVersion, err := channel.GetVersion(ctx)
if err != nil {
return nil, trace.Wrap(err)
}
// We don't want to tell the updater to upgrade to a new major we don't support yet
// This is mainly a workaround for Teleport Cloud and might be removed
// In the future when we'll have better tooling to control version channels.
targetMajor, err := parseMajorFromVersionString(targetVersion)
if err != nil {
return nil, trace.Wrap(err, "failed to process target version")
}
teleportMajor, err := parseMajorFromVersionString(teleport.Version)
if err != nil {
return nil, trace.Wrap(err, "failed to process teleport version")
}
if targetMajor > teleportMajor {
_, err = w.Write([]byte(constants.NoVersion))
h.log.Debugf("Client hit channel %s, target version (%s) major is above the proxy major (%s). Ignoring update.")
return nil, trace.Wrap(err)
}
_, err = w.Write([]byte(targetVersion))
return nil, trace.Wrap(err)
}
// automaticUpgradesCritical handles criticality requests from upgraders
func (h *Handler) automaticUpgradesCritical(w http.ResponseWriter, r *http.Request, channel *automaticupgrades.Channel) (interface{}, error) {
ctx, cancel := context.WithTimeout(r.Context(), defaultChannelTimeout)
defer cancel()
critical, err := channel.GetCritical(ctx)
if err != nil {
return nil, trace.Wrap(err)
}
response := "no"
if critical {
response = "yes"
}
_, err = w.Write([]byte(response))
return nil, trace.Wrap(err)
}
func parseMajorFromVersionString(version string) (int, error) {
version, err := versionlib.EnsureSemver(version)
if err != nil {
return 0, trace.Wrap(err, "invalid semver: %s", version)
}
majorStr := semver.Major(version)
if majorStr == "" {
return 0, trace.BadParameter("cannot detect version major")
}
major, err := strconv.Atoi(strings.TrimPrefix(majorStr, "v"))
return major, trace.Wrap(err, "cannot convert version major to int")
}