Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ jobs:
- name: Build Release Artifacts
run: |
earthly --strict --remote-cache ghcr.io/crossplane-contrib/crossplane-diff/earthly-cache:release +multiplatform-build \
--CROSSPLANE_VERSION=${{ steps.version.outputs.VERSION }}
--CROSSPLANE_DIFF_VERSION=${{ steps.version.outputs.VERSION }}

- name: Prepare Release Artifacts
run: |
Expand Down
30 changes: 15 additions & 15 deletions Earthfile
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,9 @@ e2e:
BUILD +patch-crds --CROSSPLANE_IMAGE_TAG=${CROSSPLANE_IMAGE_TAG}
COPY --dir test .
TRY
# Using a static CROSSPLANE_VERSION allows Earthly to cache E2E runs as long
# as no code changed. If the version contains a git commit (the default) the
# build layer cache is invalidated on every commit.
# Note: The crossplane-diff binary version (CROSSPLANE_DIFF_VERSION) is not
# passed here to allow Earthly to cache E2E runs as long as code doesn't change.
# If the version contains a git commit, the cache would be invalidated on every commit.
WITH DOCKER --pull crossplane/crossplane:${CROSSPLANE_IMAGE_TAG}
# TODO(negz:) Set GITHUB_ACTIONS=true and use RUN --raw-output when
# https://github.com/earthly/earthly/issues/4143 is fixed.
Expand All @@ -134,7 +134,7 @@ go-modules:
go-modules-tidy:
FROM +go-modules
CACHE --id go-build --sharing shared /root/.cache/go-build
COPY --dir cmd/ test/ .
COPY --dir cmd/ internal/ test/ .
RUN go mod tidy
RUN go mod verify
SAVE ARTIFACT go.mod AS LOCAL go.mod
Expand Down Expand Up @@ -164,12 +164,12 @@ patch-crds:
go-build:
ARG EARTHLY_GIT_SHORT_HASH
ARG EARTHLY_GIT_COMMIT_TIMESTAMP
ARG CROSSPLANE_VERSION=v0.0.0-${EARTHLY_GIT_COMMIT_TIMESTAMP}-${EARTHLY_GIT_SHORT_HASH}
ARG CROSSPLANE_DIFF_VERSION=v0.0.0-${EARTHLY_GIT_COMMIT_TIMESTAMP}-${EARTHLY_GIT_SHORT_HASH}
ARG TARGETARCH
ARG TARGETOS
ARG GOARCH=${TARGETARCH}
ARG GOOS=${TARGETOS}
ARG GOFLAGS="\"-ldflags=-s -w -X=github.com/crossplane/crossplane/internal/version.version=${CROSSPLANE_VERSION}\""
ARG LDFLAGS="-s -w -X=github.com/crossplane-contrib/crossplane-diff/internal/version.version=${CROSSPLANE_DIFF_VERSION}"
ARG CGO_ENABLED=0
ARG BIN_NAME=crossplane-diff
FROM +go-modules
Expand All @@ -178,8 +178,8 @@ go-build:
SET ext = ".exe"
END
CACHE --id go-build --sharing shared /root/.cache/go-build
COPY --dir cmd/ .
RUN go build -o ${BIN_NAME}${ext} ./cmd/diff
COPY --dir cmd/ internal/ .
RUN go build -ldflags="${LDFLAGS}" -o ${BIN_NAME}${ext} ./cmd/diff
RUN sha256sum ${BIN_NAME}${ext} | head -c 64 > ${BIN_NAME}${ext}.sha256
RUN tar -czvf ${BIN_NAME}.tar.gz ${BIN_NAME}${ext} ${BIN_NAME}${ext}.sha256
RUN sha256sum ${BIN_NAME}.tar.gz | head -c 64 > ${BIN_NAME}.tar.gz.sha256
Expand Down Expand Up @@ -219,7 +219,7 @@ go-test:
FROM +go-modules
DO github.com/earthly/lib+INSTALL_DIND
CACHE --id go-build --sharing shared /root/.cache/go-build
COPY --dir cmd/ .
COPY --dir cmd/ internal/ .
# Fetch the cluster directory from the crossplane repo at the specified tag
COPY (+fetch-crossplane-cluster/${CROSSPLANE_IMAGE_TAG} --CROSSPLANE_IMAGE_TAG=${CROSSPLANE_IMAGE_TAG}) cluster/${CROSSPLANE_IMAGE_TAG}
COPY --dir +envtest-setup/envtest /usr/local/kubebuilder/bin
Expand All @@ -245,9 +245,10 @@ go-lint:
CACHE --id go-build --sharing shared /root/.cache/go-build
RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin ${GOLANGCI_LINT_VERSION}
COPY .golangci.yml .
COPY --dir cmd/ test/ .
COPY --dir cmd/ internal/ test/ .
RUN golangci-lint run --fix
SAVE ARTIFACT cmd AS LOCAL cmd
SAVE ARTIFACT internal AS LOCAL internal
SAVE ARTIFACT test AS LOCAL test

# envtest-setup is used by other targets to setup envtest.
Expand Down Expand Up @@ -354,16 +355,15 @@ ci-codeql:
ARG CGO_ENABLED=0
ARG TARGETOS
ARG TARGETARCH
# Using a static CROSSPLANE_VERSION allows Earthly to cache E2E runs as long
# as no code changed. If the version contains a git commit (the default) the
# build layer cache is invalidated on every commit.
FROM +go-modules --CROSSPLANE_VERSION=v0.0.0-codeql
# Note: Using a static version for caching. If the version contains a git commit,
# the build layer cache would be invalidated on every commit.
FROM +go-modules
IF [ "${TARGETARCH}" = "arm64" ] && [ "${TARGETOS}" = "linux" ]
RUN --no-cache echo "CodeQL doesn't support Linux on Apple Silicon" && false
END
COPY --dir +ci-codeql-setup/codeql /codeql
CACHE --id go-build --sharing shared /root/.cache/go-build
COPY --dir cmd/ .
COPY --dir cmd/ internal/ .
RUN /codeql/codeql database create /codeqldb --language=go
RUN /codeql/codeql database analyze /codeqldb --threads=0 --format=sarif-latest --output=go.sarif --sarif-add-baseline-file-info
SAVE ARTIFACT go.sarif AS LOCAL _output/codeql/go.sarif
Expand Down
6 changes: 6 additions & 0 deletions cmd/diff/diff_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,9 @@ func runIntegrationTest(t *testing.T, testType DiffTestType, scheme *runtime.Sch

// TestDiffIntegration runs an integration test for the diff command.
func TestDiffIntegration(t *testing.T) {
// Set up logger for controller-runtime (global setup, once per test function)
tu.SetupKubeTestLogger(t)

scheme := createTestScheme()

tests := map[string]IntegrationTestCase{
Expand Down Expand Up @@ -1761,6 +1764,9 @@ Summary: 2 added`,

// TestCompDiffIntegration runs an integration test for the composition diff command.
func TestCompDiffIntegration(t *testing.T) {
// Set up logger for controller-runtime (global setup, once per test function)
tu.SetupKubeTestLogger(t)

scheme := createTestScheme()

tests := map[string]IntegrationTestCase{
Expand Down
3 changes: 1 addition & 2 deletions cmd/diff/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,12 @@ import (
"time"

"github.com/alecthomas/kong"
"github.com/crossplane-contrib/crossplane-diff/cmd/diff/version"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"sigs.k8s.io/controller-runtime/pkg/log/zap"

"github.com/crossplane/crossplane-runtime/v2/pkg/logging"

"github.com/crossplane/crossplane/v2/cmd/crank/version"
)

var _ = kong.Must(&cli{})
Expand Down
63 changes: 63 additions & 0 deletions cmd/diff/version/version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
Copyright 2023 The Crossplane Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Package version contains version cmd
package version

import (
"context"
"fmt"
"time"

"github.com/alecthomas/kong"
"github.com/crossplane-contrib/crossplane-diff/internal/version"
"github.com/pkg/errors"

xpversion "github.com/crossplane/crossplane/v2/cmd/crank/version"
)

const (
errGetCrossplaneVersion = "unable to get crossplane version"
)

// Cmd represents the version command.
type Cmd struct {
Client bool `env:"" help:"If true, shows client version only (no server required)."`
}

// Run runs the version command.
func (c *Cmd) Run(k *kong.Context) error {
_, _ = fmt.Fprintln(k.Stdout, "Client Version: "+version.New().GetVersionString())

if c.Client {
return nil
}

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

// Reuse the upstream FetchCrossplaneVersion to get the server version
vxp, err := xpversion.FetchCrossplaneVersion(ctx)
if err != nil {
return errors.Wrap(err, errGetCrossplaneVersion)
}

if vxp != "" {
_, _ = fmt.Fprintln(k.Stdout, "Server Version: "+vxp)
}

return nil
}
138 changes: 138 additions & 0 deletions cmd/diff/version/version_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/*
Copyright 2023 The Crossplane Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package version

import (
"bytes"
"strings"
"testing"

"github.com/alecthomas/kong"
)

func TestCmd_Run_ClientOnly(t *testing.T) {
tests := []struct {
name string
client bool
wantSubstring string
}{
{
name: "ClientVersionOnly",
client: true,
wantSubstring: "Client Version:",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Create a buffer to capture output
var buf bytes.Buffer

// Create a Kong instance with our buffer as stdout
k := &kong.Kong{
Stdout: &buf,
}

// Create a kong.Context with the Kong instance
ctx := &kong.Context{
Kong: k,
}

// Create the command
cmd := &Cmd{
Client: tt.client,
}

// Run the command
err := cmd.Run(ctx)
if err != nil {
t.Errorf("Run() error = %v, want nil", err)
return
}

// Check that output contains the expected substring
output := buf.String()
if !strings.Contains(output, tt.wantSubstring) {
t.Errorf("Run() output = %q, want to contain %q", output, tt.wantSubstring)
}

// Verify it only outputs client version (no server version)
if tt.client && strings.Contains(output, "Server Version:") {
t.Errorf("Run() output = %q, should not contain 'Server Version:' when client=true", output)
}
})
}
}

func TestCmd_Run_ServerVersion(t *testing.T) {
// This test verifies that when Client=false, the command attempts to fetch
// the server version. We can't easily test the full flow without a running
// cluster, so we just verify the command executes without panicking and
// returns an error (since we don't have a cluster in unit tests).
t.Run("ServerVersionAttempt", func(t *testing.T) {
var buf bytes.Buffer

k := &kong.Kong{
Stdout: &buf,
}

ctx := &kong.Context{
Kong: k,
}

cmd := &Cmd{
Client: false, // This will attempt to fetch server version
}

// We expect this to error since we don't have a cluster
err := cmd.Run(ctx)

// Verify client version was still printed
output := buf.String()
if !strings.Contains(output, "Client Version:") {
t.Errorf("Run() output = %q, want to contain 'Client Version:'", output)
}

// We expect an error trying to connect to the server
if err == nil {
// If there's no error, that means either:
// 1. We have a cluster available (unlikely in unit tests)
// 2. The server version fetch succeeded
// Either way, we should check that server version was printed
if !strings.Contains(output, "Server Version:") && err == nil {
t.Errorf("Run() with Client=false succeeded but no Server Version in output")
}
}
})
}

func TestCmd_Structure(t *testing.T) {
// Verify the Cmd struct has the expected fields
cmd := &Cmd{}

// Test that we can set Client to true
cmd.Client = true
if !cmd.Client {
t.Error("Cmd.Client field not working")
}

// Test that we can set Client to false
cmd.Client = false
if cmd.Client {
t.Error("Cmd.Client field not working")
}
}
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ toolchain go1.25.3

require (
dario.cat/mergo v1.0.2
github.com/Masterminds/semver v1.5.0
github.com/alecthomas/kong v1.12.1
github.com/crossplane/crossplane-runtime/v2 v2.1.0-rc.0
github.com/crossplane/crossplane/v2 v2.0.2
github.com/google/go-cmp v0.7.0
github.com/pkg/errors v0.9.1
github.com/sergi/go-diff v1.4.0
k8s.io/api v0.33.0
k8s.io/apiextensions-apiserver v0.33.0
Expand All @@ -24,7 +26,6 @@ require (
require (
cel.dev/expr v0.19.1 // indirect
github.com/MakeNowJust/heredoc v1.0.0 // indirect
github.com/Masterminds/semver v1.5.0 // indirect
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/chai2010/gettext-go v1.0.2 // indirect
Expand All @@ -50,7 +51,6 @@ require (
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/nxadm/tail v1.4.11 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spf13/afero v1.12.0 // indirect
github.com/stoewer/go-strcase v1.3.0 // indirect
Expand Down
Loading
Loading