diff --git a/.github/workflows/unit-tests-operator-bypass.yaml b/.github/workflows/unit-tests-integrations-bypass.yaml similarity index 72% rename from .github/workflows/unit-tests-operator-bypass.yaml rename to .github/workflows/unit-tests-integrations-bypass.yaml index 07612429699..9cefbd48674 100644 --- a/.github/workflows/unit-tests-operator-bypass.yaml +++ b/.github/workflows/unit-tests-integrations-bypass.yaml @@ -8,32 +8,38 @@ # # Note both workflows must have the same name. -name: Unit Tests (Operator) -run-name: Skip Unit Tests (Operator) - ${{ github.run_id }} - @${{ github.actor }} +name: Unit Tests (Integrations) +run-name: Skip Unit Tests (Integrations) - ${{ github.run_id }} - @${{ github.actor }} on: pull_request: paths-ignore: - '/go.mod' - '/go.sum' - - 'integrations/operator/**' + - 'integrations/**' - 'api/types/**' + - 'gen/**' - 'lib/tbot/**' + - 'Makefile' + - '.github/workflows/unit-tests-integrations.yaml' - 'build.assets/Makefile' - 'build.assets/Dockerfile*' merge_group: paths-ignore: - '/go.mod' - '/go.sum' - - 'integrations/operator/**' + - 'integrations/**' - 'api/types/**' + - 'gen/**' - 'lib/tbot/**' + - 'Makefile' + - '.github/workflows/unit-tests-integrations.yaml' - 'build.assets/Makefile' - 'build.assets/Dockerfile*' jobs: test: - name: Unit Tests (Operator) + name: Unit Tests (Integrations) runs-on: ubuntu-latest permissions: diff --git a/.github/workflows/unit-tests-operator.yaml b/.github/workflows/unit-tests-integrations.yaml similarity index 64% rename from .github/workflows/unit-tests-operator.yaml rename to .github/workflows/unit-tests-integrations.yaml index 2d177e00d14..06342cdb2d6 100644 --- a/.github/workflows/unit-tests-operator.yaml +++ b/.github/workflows/unit-tests-integrations.yaml @@ -1,5 +1,5 @@ -name: Unit Tests (Operator) -run-name: Unit Tests (Operator) - ${{ github.run_id }} - @${{ github.actor }} +name: Unit Tests (Integrations) +run-name: Unit Tests (Integrations) - ${{ github.run_id }} - @${{ github.actor }} on: push: @@ -9,26 +9,30 @@ on: paths: - '/go.mod' - '/go.sum' - - 'integrations/operator/**' + - 'integrations/**' - 'api/types/**' - 'gen/**' - 'lib/tbot/**' + - 'Makefile' + - '.github/workflows/unit-tests-integrations.yaml' - 'build.assets/Makefile' - 'build.assets/Dockerfile*' merge_group: paths: - '/go.mod' - '/go.sum' - - 'integrations/operator/**' + - 'integrations/**' - 'api/types/**' - 'gen/**' - 'lib/tbot/**' + - 'Makefile' + - '.github/workflows/unit-tests-integrations.yaml' - 'build.assets/Makefile' - 'build.assets/Dockerfile*' jobs: test: - name: Unit Tests (Operator) + name: Unit Tests (Integrations) if: ${{ !startsWith(github.head_ref, 'dependabot/') }} runs-on: ubuntu-22.04-16core @@ -46,6 +50,10 @@ jobs: - name: Prepare workspace uses: ./.github/actions/prepare-workspace - - name: Run tests + - name: Run operator tests timeout-minutes: 40 run: make test-operator + + - name: Run kube-agent-updater tests + timeout-minutes: 15 + run: make test-kube-agent-updater diff --git a/Makefile b/Makefile index f6d91e6ecbe..b5fadd1fc30 100644 --- a/Makefile +++ b/Makefile @@ -746,7 +746,7 @@ integration-root: $(TEST_LOG_DIR) $(RENDER_TESTS) # changes (or last commit). # .PHONY: lint -lint: lint-sh lint-helm lint-api lint-go lint-license lint-rust lint-tools lint-protos +lint: lint-sh lint-helm lint-api lint-kube-agent-updater lint-go lint-license lint-rust lint-tools lint-protos .PHONY: lint-tools lint-tools: lint-build-tooling lint-backport @@ -800,6 +800,11 @@ lint-api: GO_LINT_API_FLAGS ?= lint-api: cd api && golangci-lint run -c ../.golangci.yml $(GO_LINT_API_FLAGS) +.PHONY: lint-kube-agent-updater +lint-kube-agent-updater: GO_LINT_API_FLAGS ?= +lint-kube-agent-updater: + cd integrations/kube-agent-updater && golangci-lint run -c ../../.golangci.yml $(GO_LINT_API_FLAGS) + # TODO(awly): remove the `--exclude` flag after cleaning up existing scripts .PHONY: lint-sh lint-sh: SH_LINT_FLAGS ?= diff --git a/integrations/kube-agent-updater/Dockerfile b/integrations/kube-agent-updater/Dockerfile new file mode 100644 index 00000000000..cc8110a5a5e --- /dev/null +++ b/integrations/kube-agent-updater/Dockerfile @@ -0,0 +1,36 @@ +ARG BUILDBOX +# BUILDPLATFORM is provided by Docker/buildx +FROM --platform=$BUILDPLATFORM $BUILDBOX as builder + +WORKDIR /go/src/github.com/gravitational/teleport/integrations/kube-agent-updater + +# Copy the Go Modules manifests +COPY go.mod go.mod +COPY go.sum go.sum + +# Download and Cache dependencies before building and copying source +# This will prevent re-downloading the operator's dependencies if they have not changed as this +# `run` layer will be cached +RUN go mod download + +COPY cmd/ cmd/ +COPY pkg/ pkg/ + +# Compiler package should use host-triplet-agnostic name (i.e. "x86-64-linux-gnu-gcc" instead of "gcc") +# in most cases, to avoid issues on systems with multiple versions of gcc (i.e. buildboxes) +# TARGETOS and TARGETARCH are provided by Docker/buildx, but must be explicitly listed here +ARG COMPILER_NAME TARGETOS TARGETARCH + +# Build the program +# CGO is required for github.com/gravitational/teleport/lib/system +RUN echo "Targeting $TARGETOS/$TARGETARCH with CC=$COMPILER_NAME" && \ + CGO_ENABLED=1 CC=$COMPILER_NAME GOOS=$TARGETOS GOARCH=$TARGETARCH \ + go build -a -o /go/bin/teleport-kube-agent-updater github.com/gravitational/teleport/integrations/kube-agent-updater/cmd/teleport-kube-agent-updater + +# Create the image with the build operator on the $TARGETPLATFORM +# TARGETPLATFORM is provided by Docker/buildx +FROM --platform=$TARGETPLATFORM gcr.io/distroless/cc +WORKDIR / +COPY --from=builder /go/bin/teleport-kube-agent-updater . + +ENTRYPOINT ["/teleport-kube-agent-updater"] diff --git a/integrations/kube-agent-updater/Makefile b/integrations/kube-agent-updater/Makefile index c3a2af89063..634b2bd5566 100644 --- a/integrations/kube-agent-updater/Makefile +++ b/integrations/kube-agent-updater/Makefile @@ -1,7 +1,40 @@ -.PHONY: test +# Image URL to use all building/pushing image targets +IMG ?= teleport-kube-agent-updater:latest +# include BUILDBOX_VERSION, BUILDBOX and BUILDBOX_variant variables +include ../../build.assets/images.mk + +# Configure which compiler and buildbox to use +OS ?= $(shell go env GOOS) +ARCH ?= $(shell go env GOARCH) +ifeq ("$(OS)","linux") +ifeq ("$(ARCH)","amd64") +COMPILER ?= x86_64-linux-gnu-gcc +PLATFORM_BUILDBOX ?= $(BUILDBOX) +else ifeq ("$(ARCH)","386") +COMPILER ?= x86_64-linux-gnu-gcc +PLATFORM_BUILDBOX ?= $(BUILDBOX) +else ifeq ("$(ARCH)","arm") +COMPILER ?= arm-linux-gnueabihf-gcc +PLATFORM_BUILDBOX ?= $(BUILDBOX_ARM) +else ifeq ("$(ARCH)","arm64") +COMPILER ?= aarch64-linux-gnu-gcc +PLATFORM_BUILDBOX ?= $(BUILDBOX_ARM) +endif +endif + +.PHONY: test test: pkg/img/cosign_fixtures_test.go go test ./... +.PHONY: docker-build +docker-build: ## Build docker image + docker buildx build --platform="$(OS)/$(ARCH)" --build-arg BUILDBOX=$(PLATFORM_BUILDBOX) \ + --build-arg COMPILER_NAME=$(COMPILER) -t ${IMG} --load ./ -f ./Dockerfile + +.PHONY: docker-push +docker-push: ## Push docker image + docker push ${IMG} + pkg/img/cosign_fixtures_test.go: hack/cosign-fixtures.go go run hack/cosign-fixtures.go | gofmt > pkg/img/cosign_fixtures_test.go diff --git a/integrations/kube-agent-updater/cmd/teleport-kube-agent-updater/main.go b/integrations/kube-agent-updater/cmd/teleport-kube-agent-updater/main.go index 1383101883e..8627bafae84 100644 --- a/integrations/kube-agent-updater/cmd/teleport-kube-agent-updater/main.go +++ b/integrations/kube-agent-updater/cmd/teleport-kube-agent-updater/main.go @@ -43,10 +43,6 @@ var ( scheme = runtime.NewScheme() ) -const ( - namespace = "namespace" -) - func init() { SchemeBuilder.Register( &appsv1.Deployment{}, diff --git a/integrations/kube-agent-updater/hack/cosign-fixtures.go b/integrations/kube-agent-updater/hack/cosign-fixtures.go index 4b3e0be5e37..a0286ba86d4 100644 --- a/integrations/kube-agent-updater/hack/cosign-fixtures.go +++ b/integrations/kube-agent-updater/hack/cosign-fixtures.go @@ -418,6 +418,9 @@ func renderFixtures(keys *cosign.KeysBytes, layers []v1.Layer, manifests map[str return "", trace.Wrap(err) } contentReader, err := layer.Uncompressed() + if err != nil { + return "", trace.Wrap(err) + } content, err := io.ReadAll(contentReader) if err != nil { return "", trace.Wrap(err) @@ -472,7 +475,7 @@ func generateSignedManifest(scenario string, signer digestedRefSigner, keys ...* Layers: []v1.Descriptor{layerDesc}, } - // Generatin a manifest referencing the test layers + // Generating a manifest referencing the test layers _, _, manifestDigest, err := contentSizeAndHash(manifest) if err != nil { return nil, nil, v1.Hash{}, trace.Wrap(err) @@ -531,6 +534,9 @@ func generateSignedIndex(scenario string, signer digestedRefSigner, keys ...*cos Architecture: "amd64", OS: "linux", }) + if err != nil { + return nil, nil, v1.Hash{}, trace.Wrap(err) + } // Referencing both manifests in an index index := v1.IndexManifest{ diff --git a/integrations/kube-agent-updater/pkg/basichttp/servermock.go b/integrations/kube-agent-updater/pkg/basichttp/servermock.go index 44f3d1d416a..fa245248d4f 100644 --- a/integrations/kube-agent-updater/pkg/basichttp/servermock.go +++ b/integrations/kube-agent-updater/pkg/basichttp/servermock.go @@ -36,7 +36,7 @@ type ServerMock struct { path string } -// SetResponse sets the ServerMock's reponse. +// SetResponse sets the ServerMock's response. func (m *ServerMock) SetResponse(t *testing.T, code int, response string) { m.t = t m.code = code diff --git a/integrations/kube-agent-updater/pkg/controller/suite_test.go b/integrations/kube-agent-updater/pkg/controller/suite_test.go index 2b3d11b76ea..446c3c19152 100644 --- a/integrations/kube-agent-updater/pkg/controller/suite_test.go +++ b/integrations/kube-agent-updater/pkg/controller/suite_test.go @@ -17,74 +17,9 @@ limitations under the License. package controller import ( - "context" - "testing" - - "github.com/stretchr/testify/require" "golang.org/x/exp/rand" - appsv1 "k8s.io/api/apps/v1" - core "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - corescheme "k8s.io/client-go/kubernetes/scheme" - ctrl "sigs.k8s.io/controller-runtime" - kclient "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/envtest" - "sigs.k8s.io/controller-runtime/pkg/manager" - runtimescheme "sigs.k8s.io/controller-runtime/pkg/scheme" ) -var ( - SchemeBuilder = &runtimescheme.Builder{GroupVersion: appsv1.SchemeGroupVersion} - scheme = runtime.NewScheme() -) - -func init() { - SchemeBuilder.Register( - &appsv1.Deployment{}, - &appsv1.DeploymentList{}, - &appsv1.StatefulSet{}, - &appsv1.StatefulSetList{}, - ) - utilruntime.Must(SchemeBuilder.AddToScheme(scheme)) -} - -func setupTest(t *testing.T) (manager.Manager, kclient.Client, string) { - testEnv := &envtest.Environment{} - cfg, err := testEnv.Start() - - require.NoError(t, err) - require.NotNil(t, cfg) - - namespace := validRandomResourceName("ns-") - - k8sClient, err := kclient.New(cfg, kclient.Options{Scheme: corescheme.Scheme}) - require.NoError(t, err) - ns := &core.Namespace{ - ObjectMeta: metav1.ObjectMeta{Name: namespace}, - } - - ctx := context.Background() - - err = k8sClient.Create(ctx, ns) - require.NoError(t, err) - - t.Cleanup(func() { - err = testEnv.Stop() - require.NoError(t, err) - }) - - mgr, err := ctrl.NewManager(cfg, ctrl.Options{ - Scheme: scheme, - MetricsBindAddress: "0", - }) - require.NoError(t, err) - - return mgr, k8sClient, namespace - -} - var letterRunes = []rune("abcdefghijklmnopqrstuvwxyz1234567890") func validRandomResourceName(prefix string) string { diff --git a/integrations/kube-agent-updater/pkg/img/cosign_test.go b/integrations/kube-agent-updater/pkg/img/cosign_test.go index 5af92b66fc5..71b4f22e204 100644 --- a/integrations/kube-agent-updater/pkg/img/cosign_test.go +++ b/integrations/kube-agent-updater/pkg/img/cosign_test.go @@ -65,6 +65,7 @@ func Test_cosignKeyValidator_ValidateAndResolveDigest(t *testing.T) { resp, err := testRegistry.Client().Do(req) require.NoError(t, err) require.Equal(t, http.StatusCreated, resp.StatusCode) + require.NoError(t, resp.Body.Close()) } for manifest, contents := range manifests { u, err := url.Parse(testRegistry.URL + "/v2/testrepo/manifests/" + manifest) @@ -77,10 +78,11 @@ func Test_cosignKeyValidator_ValidateAndResolveDigest(t *testing.T) { resp, err := testRegistry.Client().Do(req) require.NoError(t, err) require.Equal(t, http.StatusCreated, resp.StatusCode) + require.NoError(t, resp.Body.Close()) } // Build a validator - pubKey, err := cryptoutils.UnmarshalPEMToPublicKey([]byte(publicKey)) + pubKey, err := cryptoutils.UnmarshalPEMToPublicKey(publicKey) require.NoError(t, err) skid, err := cryptoutils.SKID(pubKey) require.NoError(t, err) diff --git a/integrations/kube-agent-updater/pkg/img/doc.go b/integrations/kube-agent-updater/pkg/img/doc.go index 5158d7c9757..6b4e737e7f6 100644 --- a/integrations/kube-agent-updater/pkg/img/doc.go +++ b/integrations/kube-agent-updater/pkg/img/doc.go @@ -43,5 +43,4 @@ limitations under the License. // `teleport/integrations/kube-agent-updater/hack/cosign-fixtures.go`. The // executable generates a go file declaring all blobs and manifests to be // loaded in the test registry. - package img diff --git a/integrations/kube-agent-updater/pkg/maintenance/basichttp_test.go b/integrations/kube-agent-updater/pkg/maintenance/basichttp_test.go index d573c479e15..b25b5e948d4 100644 --- a/integrations/kube-agent-updater/pkg/maintenance/basichttp_test.go +++ b/integrations/kube-agent-updater/pkg/maintenance/basichttp_test.go @@ -34,8 +34,8 @@ const basicHTTPTestPath = "/v1/cloud-stable" func Test_basicHTTPMaintenanceClient_Get(t *testing.T) { mock := basichttp.NewServerMock(basicHTTPTestPath + "/" + constants.MaintenancePath) t.Cleanup(mock.Srv.Close) - serverUrl, err := url.Parse(mock.Srv.URL) - serverUrl.Path = basicHTTPTestPath + serverURL, err := url.Parse(mock.Srv.URL) + serverURL.Path = basicHTTPTestPath require.NoError(t, err) ctx := context.Background() @@ -94,7 +94,7 @@ func Test_basicHTTPMaintenanceClient_Get(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { b := &basicHTTPMaintenanceClient{ - baseURL: serverUrl, + baseURL: serverURL, client: &basichttp.Client{Client: mock.Srv.Client()}, } mock.SetResponse(t, tt.statusCode, tt.response) diff --git a/integrations/kube-agent-updater/pkg/maintenance/mock.go b/integrations/kube-agent-updater/pkg/maintenance/mock.go index 66ef8571f0f..0c0c6b5738d 100644 --- a/integrations/kube-agent-updater/pkg/maintenance/mock.go +++ b/integrations/kube-agent-updater/pkg/maintenance/mock.go @@ -39,7 +39,7 @@ func (m TriggerMock) CanStart(_ context.Context, _ client.Object) (bool, error) return m.canStart, nil } -// Default returns the default behaviour if the trigger fails. This cannot +// Default returns the default behavior if the trigger fails. This cannot // happen for a TriggerMock and is here solely to implement the Trigger // interface. func (m TriggerMock) Default() bool { diff --git a/integrations/kube-agent-updater/pkg/maintenance/window.go b/integrations/kube-agent-updater/pkg/maintenance/window.go index 0dbf33a2ee2..37575eaca5a 100644 --- a/integrations/kube-agent-updater/pkg/maintenance/window.go +++ b/integrations/kube-agent-updater/pkg/maintenance/window.go @@ -97,7 +97,7 @@ type kubeScheduleRepr struct { // it has no upcoming or ongoing maintenance window, or if it contains a window // whose start is after its end. This could happen if the agent looses // connectivity to its cluster or if we have a bug in the window calculation. -// In this case we don't want to honour the schedule and will consider the +// In this case we don't want to honor the schedule and will consider the // agent is not working properly. func (s kubeScheduleRepr) isValid(now time.Time) bool { valid := false diff --git a/integrations/kube-agent-updater/pkg/version/basichttp_test.go b/integrations/kube-agent-updater/pkg/version/basichttp_test.go index 638dd23dada..559cd0dc5e7 100644 --- a/integrations/kube-agent-updater/pkg/version/basichttp_test.go +++ b/integrations/kube-agent-updater/pkg/version/basichttp_test.go @@ -34,8 +34,8 @@ const basicHTTPTestPath = "/v1/cloud-stable" func Test_basicHTTPVersionClient_Get(t *testing.T) { mock := basichttp.NewServerMock(basicHTTPTestPath + "/" + constants.VersionPath) t.Cleanup(mock.Srv.Close) - serverUrl, err := url.Parse(mock.Srv.URL) - serverUrl.Path = basicHTTPTestPath + serverURL, err := url.Parse(mock.Srv.URL) + serverURL.Path = basicHTTPTestPath require.NoError(t, err) ctx := context.Background() @@ -89,7 +89,7 @@ func Test_basicHTTPVersionClient_Get(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { b := &basicHTTPVersionClient{ - baseURL: serverUrl, + baseURL: serverURL, client: &basichttp.Client{Client: mock.Srv.Client()}, } mock.SetResponse(t, tt.statusCode, tt.response) diff --git a/integrations/kube-agent-updater/pkg/version/versionget.go b/integrations/kube-agent-updater/pkg/version/versionget.go index 6515213493b..ace71623b1a 100644 --- a/integrations/kube-agent-updater/pkg/version/versionget.go +++ b/integrations/kube-agent-updater/pkg/version/versionget.go @@ -39,7 +39,7 @@ func ValidVersionChange(ctx context.Context, current, next string) bool { log := ctrllog.FromContext(ctx).V(1) // Cannot upgrade to a non-valid version if !semver.IsValid(next) { - log.Error(trace.BadParameter("next verison is not following semver"), "version change is invalid", "nextVersion", next) + log.Error(trace.BadParameter("next version is not following semver"), "version change is invalid", "nextVersion", next) return false } switch semver.Compare(next, current) {