diff --git a/controller/kongplugininstallation/controller.go b/controller/kongplugininstallation/controller.go index e8ee4534..2ec4dee2 100644 --- a/controller/kongplugininstallation/controller.go +++ b/controller/kongplugininstallation/controller.go @@ -1,6 +1,7 @@ package kongplugininstallation import ( + "bytes" "context" "errors" "fmt" @@ -11,7 +12,7 @@ import ( k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - "oras.land/oras-go/v2/registry/remote/credentials" + orascreds "oras.land/oras-go/v2/registry/remote/credentials" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" @@ -99,7 +100,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu } log.Trace(logger, "managing KongPluginInstallation resource") - var credentialsStore credentials.Store + var credentialsStore orascreds.Store if kpi.Spec.ImagePullSecretRef != nil { log.Trace(logger, "getting secret for KongPluginInstallation resource") kpiNamespace := gatewayv1.Namespace(kpi.Namespace) @@ -142,7 +143,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu ) } var err error - credentialsStore, err = image.CredentialsStoreFromString(string(secretData)) + credentialsStore, err = orascreds.NewReadOnlyFileStore(bytes.NewReader(secretData)) if err != nil { return ctrl.Result{}, setStatusConditionFailedForKongPluginInstallation(ctx, r.Client, &kpi, fmt.Sprintf("can't parse secret: %q data: %s", secretNN, err)) } diff --git a/controller/kongplugininstallation/image/image.go b/controller/kongplugininstallation/image/image.go index 3372c1c9..0aebaa89 100644 --- a/controller/kongplugininstallation/image/image.go +++ b/controller/kongplugininstallation/image/image.go @@ -7,7 +7,6 @@ import ( "errors" "fmt" "io" - "os" "path/filepath" "strings" "sync" @@ -118,26 +117,6 @@ func FetchPlugin(ctx context.Context, imageURL string, credentialsStore credenti return extractKongPluginFromLayer(contentOfLayerWithPlugin) } -// CredentialsStoreFromString expects content of typical configuration as a string, described -// in https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry -// and returns credentials.Store. -// This is typical way how private registries are used with Docker and Kubernetes. -func CredentialsStoreFromString(s string) (credentials.Store, error) { - // TODO: Now we create temporary file, which is not great and should be changed, - // but it's the only way to use credentials.NewFileStore(...) which robustly - // parses config.json (format used by Docker and Kubernetes). - tmpFile, err := os.CreateTemp("", "credentials") - if err != nil { - return nil, fmt.Errorf("failed to create temporary file: %w", err) - } - defer os.Remove(tmpFile.Name()) - defer tmpFile.Close() - if _, err = tmpFile.WriteString(s); err != nil { - return nil, fmt.Errorf("failed to write credentials to file: %w", err) - } - return credentials.NewFileStore(tmpFile.Name()) -} - type sizeLimitBytes int64 func (sl sizeLimitBytes) int64() int64 { diff --git a/controller/kongplugininstallation/image/image_test.go b/controller/kongplugininstallation/image/image_test.go index 0d328faf..b897b2cd 100644 --- a/controller/kongplugininstallation/image/image_test.go +++ b/controller/kongplugininstallation/image/image_test.go @@ -2,61 +2,16 @@ package image_test import ( "context" + "strings" "testing" "github.com/stretchr/testify/require" - "oras.land/oras-go/v2/registry/remote/auth" - "oras.land/oras-go/v2/registry/remote/credentials" + orascreds "oras.land/oras-go/v2/registry/remote/credentials" "github.com/kong/gateway-operator/controller/kongplugininstallation/image" "github.com/kong/gateway-operator/test/integration" ) -func TestCredentialsStoreFromString(t *testing.T) { - testCases := []struct { - name string - credentials string - expectedErrorMsg string - expectedCredentials func(t *testing.T, cs credentials.Store) - }{ - { - name: "invalid credentials", - credentials: "foo", - expectedErrorMsg: "invalid config format:", - }, - { - name: "valid credentials", - // Field auth is base64 encoded "test:test". - credentials: ` - { - "auths": { - "ghcr.io": { - "auth": "dGVzdDp0ZXN0" - } - } - }`, - expectedCredentials: func(t *testing.T, cs credentials.Store) { - t.Helper() - require.NotNil(t, cs) - c, err := cs.Get(context.Background(), "ghcr.io") - require.NoError(t, err) - require.Equal(t, auth.Credential{Username: "test", Password: "test", RefreshToken: "", AccessToken: ""}, c) - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - credsStore, err := image.CredentialsStoreFromString(tc.credentials) - if tc.expectedCredentials != nil { - tc.expectedCredentials(t, credsStore) - } else { - require.ErrorContains(t, err, tc.expectedErrorMsg) - } - }) - } -} - func TestFetchPluginContent(t *testing.T) { t.Log("This test accesses container registries on public internet") @@ -94,7 +49,7 @@ func TestFetchPluginContent(t *testing.T) { t.Skip("skipping - no credentials provided") } - credsStore, err := image.CredentialsStoreFromString(credentials) + credsStore, err := orascreds.NewReadOnlyFileStore(strings.NewReader(credentials)) require.NoError(t, err) plugin, err := image.FetchPlugin( diff --git a/go.mod b/go.mod index 6ecfbda8..a424a287 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,8 @@ go 1.23.2 // This retraction is to prevent it from being used and from breaking builds of dependent projects. retract v1.2.2 +replace oras.land/oras-go/v2 => github.com/programmer04/oras-go/v2 v2.5.1-0.20250109105054-83d5c64b9a01 + require ( github.com/Kong/sdk-konnect-go v0.1.18 github.com/Masterminds/semver v1.5.0 diff --git a/go.sum b/go.sum index d5b30b8a..749cc4af 100644 --- a/go.sum +++ b/go.sum @@ -388,6 +388,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg= github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= +github.com/programmer04/oras-go/v2 v2.5.1-0.20250109105054-83d5c64b9a01 h1:xywm1lFq4n4HpBmEM2Wciq6R5MSg4CYTZlKnxFn1THU= +github.com/programmer04/oras-go/v2 v2.5.1-0.20250109105054-83d5c64b9a01/go.mod h1:ecS2SG90/ztmqyrxF98+K4Uxq88AqdpZti6DP3g3FZc= github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI= github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -687,8 +689,6 @@ k8s.io/kubernetes v1.32.0 h1:4BDBWSolqPrv8GC3YfZw0CJvh5kA1TPnoX0FxDVd+qc= k8s.io/kubernetes v1.32.0/go.mod h1:tiIKO63GcdPRBHW2WiUFm3C0eoLczl3f7qi56Dm1W8I= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -oras.land/oras-go/v2 v2.5.0 h1:o8Me9kLY74Vp5uw07QXPiitjsw7qNXi8Twd+19Zf02c= -oras.land/oras-go/v2 v2.5.0/go.mod h1:z4eisnLP530vwIOUOJeBIj0aGI0L1C3d53atvCBqZHg= sigs.k8s.io/controller-runtime v0.19.4 h1:SUmheabttt0nx8uJtoII4oIP27BVVvAKFvdvGFwV/Qo= sigs.k8s.io/controller-runtime v0.19.4/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= sigs.k8s.io/gateway-api v1.2.1 h1:fZZ/+RyRb+Y5tGkwxFKuYuSRQHu9dZtbjenblleOLHM=