diff --git a/cmd/cluster-authentication-operator-tests-ext/main.go b/cmd/cluster-authentication-operator-tests-ext/main.go index 2c7cf8e242..1abf407b66 100644 --- a/cmd/cluster-authentication-operator-tests-ext/main.go +++ b/cmd/cluster-authentication-operator-tests-ext/main.go @@ -17,6 +17,7 @@ import ( _ "github.com/openshift/cluster-authentication-operator/test/e2e-encryption-kms" _ "github.com/openshift/cluster-authentication-operator/test/e2e-encryption-perf" _ "github.com/openshift/cluster-authentication-operator/test/e2e-encryption-rotation" + _ "github.com/openshift/cluster-authentication-operator/test/e2e-oidc" "k8s.io/klog/v2" ) @@ -63,25 +64,14 @@ func prepareOperatorTestsRegistry() (*oteextension.Registry, error) { registry := oteextension.NewRegistry() extension := oteextension.NewExtension("openshift", "payload", "cluster-authentication-operator") - // The following suite runs tests that verify the operator's behaviour. - // This suite is executed only on pull requests targeting this repository. - // Tests that are not tagged with [Serial] and have any of [Operator], [Templates], [Tokens] are included in this suite. + // The following suite runs tests that verify the operator's behaviour in parallel. + // Includes: [Certs], [Routes], [Templates], [Tokens] tests + // These tests can run concurrently without conflicts. extension.AddSuite(oteextension.Suite{ Name: "openshift/cluster-authentication-operator/operator/parallel", Parallelism: 4, Qualifiers: []string{ - `!name.contains("[Serial]") && (name.contains("[Operator]") || name.contains("[Templates]") || name.contains("[Tokens]"))`, - }, - }) - - // The following suite runs tests that must execute serially (one at a time) - // because they modify cluster-wide resources like OAuth configuration. - // Tests tagged with [Serial] and any of [Operator], [OIDC], [Templates], [Tokens] are included in this suite. - extension.AddSuite(oteextension.Suite{ - Name: "openshift/cluster-authentication-operator/operator/serial", - Parallelism: 1, - Qualifiers: []string{ - `name.contains("[Serial]") && (name.contains("[Operator]") || name.contains("[OIDC]") || name.contains("[Templates]") || name.contains("[Tokens]"))`, + `name.contains("[Parallel]") && !name.contains("[Disruptive]") && !name.contains("[ExternalOIDC]") && !name.contains("KMS")`, }, }) @@ -95,23 +85,25 @@ func prepareOperatorTestsRegistry() (*oteextension.Registry, error) { ClusterStability: oteextension.ClusterStabilityDisruptive, }) - // ClusterStability set to Disruptive: encryption tests trigger API server rollouts. + // The following suite runs all serial tests (encryption + OAuth/IDP) in one suite. + // ClusterStability set to Disruptive: encryption tests trigger API server rollouts and IDP tests put operator in Degraded state during cleanup. extension.AddSuite(oteextension.Suite{ - Name: "openshift/cluster-authentication-operator/operator-encryption/serial", + Name: "openshift/cluster-authentication-operator/operator/serial", Parallelism: 1, ClusterStability: oteextension.ClusterStabilityDisruptive, Qualifiers: []string{ - `name.contains("[Encryption]") && name.contains("[Serial]") && !name.contains("Rotation") && !name.contains("Perf") && !name.contains("KMS")`, + `name.contains("[Serial]") && !name.contains("[Disruptive]") && !name.contains("[ExternalOIDC]") && !name.contains("KMS")`, }, }) - // ClusterStability set to Disruptive: encryption perf tests trigger API server rollouts. + // The following suite runs external OIDC tests that authenticate directly via external OIDC + // (bypassing OpenShift's IntegratedOAuth). These are long-running tests in their own suite. extension.AddSuite(oteextension.Suite{ - Name: "openshift/cluster-authentication-operator/operator-encryption-perf/serial", + Name: "openshift/cluster-authentication-operator/operator-external-oidc/serial", Parallelism: 1, ClusterStability: oteextension.ClusterStabilityDisruptive, Qualifiers: []string{ - `name.contains("[Encryption]") && name.contains("[Serial]") && name.contains("Perf")`, + `name.contains("[ExternalOIDC]")`, }, }) @@ -124,16 +116,6 @@ func prepareOperatorTestsRegistry() (*oteextension.Registry, error) { }, }) - // ClusterStability set to Disruptive: encryption rotation triggers API server rollouts. - extension.AddSuite(oteextension.Suite{ - Name: "openshift/cluster-authentication-operator/operator-encryption-rotation/serial", - Parallelism: 1, - ClusterStability: oteextension.ClusterStabilityDisruptive, - Qualifiers: []string{ - `name.contains("[Encryption]") && name.contains("[Serial]") && name.contains("Rotation")`, - }, - }) - specs, err := oteginkgo.BuildExtensionTestSpecsFromOpenShiftGinkgoSuite() if err != nil { return nil, fmt.Errorf("couldn't build extension test specs from ginkgo: %w", err) diff --git a/test/e2e-encryption-perf/encryption_perf.go b/test/e2e-encryption-perf/encryption_perf.go index 9f8226adbb..5e7c6e2091 100644 --- a/test/e2e-encryption-perf/encryption_perf.go +++ b/test/e2e-encryption-perf/encryption_perf.go @@ -26,7 +26,7 @@ const ( ) var _ = g.Describe("[sig-auth] authentication operator", func() { - g.It("[Encryption][Serial] TestPerfEncryptionTypeAESCBC", func(ctx context.Context) { + g.It("[Encryption][Serial] TestPerfEncryptionTypeAESCBC [Timeout:30m]", func(ctx context.Context) { testPerfEncryptionTypeAESCBC(ctx, g.GinkgoTB()) }) }) diff --git a/test/e2e-encryption/encryption.go b/test/e2e-encryption/encryption.go index c9a29ff4b1..04ccaa889a 100644 --- a/test/e2e-encryption/encryption.go +++ b/test/e2e-encryption/encryption.go @@ -6,9 +6,16 @@ import ( "time" g "github.com/onsi/ginkgo/v2" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/wait" configv1 "github.com/openshift/api/config/v1" + configclient "github.com/openshift/client-go/config/clientset/versioned" + test "github.com/openshift/cluster-authentication-operator/test/library" library "github.com/openshift/library-go/test/library/encryption" ) @@ -17,7 +24,7 @@ var _ = g.Describe("[sig-auth] authentication operator", func() { testEncryptionTypeIdentity(ctx, g.GinkgoTB()) }) - g.It("[Encryption][Serial] TestEncryptionTypeUnset", func(ctx context.Context) { + g.It("[Encryption][Serial] TestEncryptionTypeUnset [Timeout:30m]", func(ctx context.Context) { testEncryptionTypeUnset(ctx, g.GinkgoTB()) }) @@ -39,7 +46,8 @@ func testEncryptionTypeIdentity(ctx context.Context, tt testing.TB) { } func testEncryptionTypeUnset(ctx context.Context, tt testing.TB) { - library.TestEncryptionTypeUnset(ctx, tt, library.BasicScenario{ + // Custom implementation that waits for migration to complete before asserting + scenario := library.BasicScenario{ Namespace: "openshift-config-managed", LabelSelector: "encryption.apiserver.operator.openshift.io/component=openshift-oauth-apiserver", EncryptionConfigSecretName: "encryption-config-openshift-oauth-apiserver", @@ -47,7 +55,30 @@ func testEncryptionTypeUnset(ctx context.Context, tt testing.TB) { OperatorNamespace: "openshift-authentication-operator", TargetGRs: library.AuthTargetGRs, AssertFunc: library.AssertTokens, - }) + } + + tt.Logf("Starting custom TestEncryptionTypeUnset with migration wait logic") + + e := library.NewE(tt, library.PrintEventsOnFailure(scenario.OperatorNamespace)) + + // Step 1: Set encryption type to unset and wait for key changes + clientSet := library.SetAndWaitForEncryptionType(ctx, e, library.EncryptionProvider{}, + scenario.TargetGRs, scenario.Namespace, scenario.LabelSelector) + + // Step 2: Wait for StorageVersionMigration to complete (this is the missing piece!) + tt.Logf("Waiting for OAuth token migration to complete...") + waitForOAuthMigrationComplete(ctx, tt, clientSet, 20*time.Minute) + + // Step 3: Wait for authentication operator to stop progressing + tt.Logf("Waiting for authentication operator to become ready...") + waitForAuthOperatorReady(ctx, tt, clientSet, 20*time.Minute) + + // Step 4: Now safe to assert - data should be unencrypted + tt.Logf("Asserting tokens are unencrypted...") + scenario.AssertFunc(e, clientSet, configv1.EncryptionTypeIdentity, + scenario.Namespace, scenario.LabelSelector) + + tt.Logf("TestEncryptionTypeUnset completed successfully") } func testEncryptionTurnOnAndOff(ctx context.Context, tt testing.TB) { @@ -75,3 +106,121 @@ func testEncryptionTurnOnAndOff(ctx context.Context, tt testing.TB) { }, }) } + +// waitForOAuthMigrationComplete waits for StorageVersionMigration resources to complete +// This is critical because encryption key changes happen quickly (2-3 min) but data +// migration can take 8-15 minutes. Without this, assertions fail prematurely. +func waitForOAuthMigrationComplete(ctx context.Context, t testing.TB, clientSet library.ClientSet, timeout time.Duration) { + t.Helper() + + migrationNames := []string{ + "encryption-migration-oauth.openshift.io-oauthaccesstokens", + "encryption-migration-oauth.openshift.io-oauthauthorizetokens", + } + + // StorageVersionMigration GVR + gvr := schema.GroupVersionResource{ + Group: "migration.k8s.io", + Version: "v1alpha1", + Resource: "storageversionmigrations", + } + + for _, migrationName := range migrationNames { + t.Logf("Waiting for StorageVersionMigration %q to complete (timeout: %v)", migrationName, timeout) + + err := wait.PollImmediate(30*time.Second, timeout, func() (bool, error) { + // Get the StorageVersionMigration resource using dynamic client + unstructuredObj, err := clientSet.DynamicClient.Resource(gvr).Get(ctx, migrationName, metav1.GetOptions{}) + if err != nil { + // If not found, migration may not have started yet or already completed + t.Logf(" StorageVersionMigration %q not found or error: %v (will retry)", migrationName, err) + return false, nil + } + + // Check status conditions + status, found, err := unstructured.NestedMap(unstructuredObj.Object, "status") + if err != nil || !found { + t.Logf(" StorageVersionMigration %q has no status yet (will retry)", migrationName) + return false, nil + } + + conditions, found, err := unstructured.NestedSlice(status, "conditions") + if err != nil || !found { + t.Logf(" StorageVersionMigration %q has no conditions yet (will retry)", migrationName) + return false, nil + } + + // Check for Succeeded=True or Running=False + for _, condObj := range conditions { + cond, ok := condObj.(map[string]interface{}) + if !ok { + continue + } + + condType, _, _ := unstructured.NestedString(cond, "type") + condStatus, _, _ := unstructured.NestedString(cond, "status") + + if condType == "Succeeded" && condStatus == "True" { + t.Logf(" ✓ StorageVersionMigration %q succeeded", migrationName) + return true, nil + } + + if condType == "Running" && condStatus == "False" { + // Check if it succeeded or failed + reason, _, _ := unstructured.NestedString(cond, "reason") + if reason == "Succeeded" || reason == "" { + t.Logf(" ✓ StorageVersionMigration %q completed (Running=False)", migrationName) + return true, nil + } + t.Logf(" StorageVersionMigration %q stopped running but reason: %s", migrationName, reason) + return false, nil + } + } + + t.Logf(" StorageVersionMigration %q still in progress...", migrationName) + return false, nil + }) + + if err != nil { + t.Logf("WARNING: Failed to confirm StorageVersionMigration %q completion: %v", migrationName, err) + t.Logf("Continuing anyway as migration may have already completed...") + } + } +} + +// waitForAuthOperatorReady waits for the authentication ClusterOperator to stop progressing +// This ensures all encryption-related changes have been fully rolled out before assertions. +func waitForAuthOperatorReady(ctx context.Context, t testing.TB, clientSet library.ClientSet, timeout time.Duration) { + t.Helper() + + t.Logf("Waiting for authentication ClusterOperator Progressing=False (timeout: %v)", timeout) + + // Get a config client + configClient, err := configclient.NewForConfig(test.NewClientConfigForTest(t)) + require.NoError(t, err) + + err = wait.PollImmediate(30*time.Second, timeout, func() (bool, error) { + co, err := configClient.ConfigV1().ClusterOperators().Get(ctx, "authentication", metav1.GetOptions{}) + if err != nil { + t.Logf(" Error getting authentication ClusterOperator: %v (will retry)", err) + return false, nil + } + + for _, condition := range co.Status.Conditions { + if condition.Type == "Progressing" { + if condition.Status == "False" { + t.Logf(" ✓ Authentication operator is no longer progressing") + return true, nil + } + t.Logf(" Authentication operator still progressing: %s (reason: %s)", + condition.Message, condition.Reason) + return false, nil + } + } + + t.Logf(" No Progressing condition found (will retry)") + return false, nil + }) + + require.NoError(t, err, "Timed out waiting for authentication operator to stop progressing") +} diff --git a/test/e2e-oidc/external_oidc.go b/test/e2e-oidc/external_oidc.go new file mode 100644 index 0000000000..b52a208dd4 --- /dev/null +++ b/test/e2e-oidc/external_oidc.go @@ -0,0 +1,1262 @@ +package e2e_oidc + +import ( + "context" + "crypto/rsa" + "crypto/tls" + "encoding/base64" + "encoding/json" + "fmt" + "io" + "math/big" + "net/http" + "strings" + "testing" + "time" + + configv1 "github.com/openshift/api/config/v1" + "github.com/openshift/api/features" + operatorv1 "github.com/openshift/api/operator/v1" + routev1 "github.com/openshift/api/route/v1" + configclient "github.com/openshift/client-go/config/clientset/versioned" + oauthclient "github.com/openshift/client-go/oauth/clientset/versioned" + operatorversionedclient "github.com/openshift/client-go/operator/clientset/versioned" + routeclient "github.com/openshift/client-go/route/clientset/versioned" + "github.com/openshift/cluster-authentication-operator/pkg/operator" + test "github.com/openshift/cluster-authentication-operator/test/library" + "github.com/openshift/library-go/pkg/operator/genericoperatorclient" + "github.com/openshift/library-go/pkg/operator/v1helpers" + "github.com/stretchr/testify/require" + utilerrors "k8s.io/apimachinery/pkg/util/errors" + apiregistrationclient "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset" + "k8s.io/utils/clock" + "k8s.io/utils/ptr" + + authenticationv1 "k8s.io/api/authentication/v1" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/apiserver/pkg/storage/names" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/dynamic/dynamicinformer" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + + "github.com/golang-jwt/jwt/v5" + g "github.com/onsi/ginkgo/v2" +) + +var _ = g.Describe("[sig-auth] authentication operator", func() { + g.It("[ExternalOIDC] TestExternalOIDCWithKeycloak [Timeout:3h]", func(ctx context.Context) { + testExternalOIDCWithKeycloak(ctx, g.GinkgoTB()) + }) +}) + +const ( + oidcClientId = "admin-cli" + oidcGroupsClaim = "groups" + oidcGroupsPrefix = "" + + managedNS = "openshift-config-managed" + authCM = "auth-config" +) + +func testExternalOIDCWithKeycloak(ctx context.Context, t testing.TB) { + testCtx := ctx + testClient, err := newTestClient(t, testCtx) + require.NoError(t, err) + + checkFeatureGatesOrSkip(t, testCtx, testClient.configClient, features.FeatureGateExternalOIDC, features.FeatureGateExternalOIDCWithAdditionalClaimMappings) + + newExternalOIDCArchitectureEnabled := featureGateEnabled(testCtx, testClient.configClient, features.FeatureGateExternalOIDCExternalClaimsSourcing) + + // post-test cluster cleanup + var cleanups []func() + defer test.IDPCleanupWrapper(func() { + t.Logf("cleaning up after test") + ts := time.Now() + for _, c := range cleanups { + c() + } + t.Logf("cleanup completed after %s", time.Since(ts)) + })() + + origAuthSpec := (*testClient.getAuth(t, testCtx)).Spec.DeepCopy() + cleanups = append(cleanups, func() { + kasOriginalRevision := testClient.kasLatestAvailableRevision(t, testCtx) + + err := testClient.authResourceRollback(testCtx, origAuthSpec) + require.NoError(t, err, "failed to rollback auth resource during cleanup") + + // KAS should not need to perform a rollout when the new architecture is enabled. + // The oauth-apiserver will, but that should be fairly quick and handled by cluster operator + // stability checks prior to running tests. + if !newExternalOIDCArchitectureEnabled { + err = test.WaitForNewKASRollout(t, testCtx, testClient.operatorConfigClient.OperatorV1().KubeAPIServers(), kasOriginalRevision) + require.NoError(t, err, "failed to wait for KAS rollout during cleanup") + } + + testClient.validateOAuthState(t, testCtx, false, newExternalOIDCArchitectureEnabled) + }) + + // keycloak setup + var idpName string + var kcClient *test.KeycloakClient + kcClient, idpName, c := test.AddKeycloakIDP(t, testClient.kubeConfig, true) + cleanups = append(cleanups, c...) + t.Logf("keycloak Admin URL configured") + + // default-ingress-cert is copied to openshift-config and used as the CA for the IdP + // see test/library/idpdeployment.go:332 + caBundleName := idpName + "-ca" + idpURL := kcClient.IssuerURL() + + // run tests + + testSpec := authSpecForOIDCProvider(idpName, idpURL, caBundleName, oidcGroupsClaim, oidcClientId) + + typeOAuth := ptr.To(configv1.AuthenticationTypeIntegratedOAuth) + typeOIDC := ptr.To(configv1.AuthenticationTypeOIDC) + operatorAvailable := []configv1.ClusterOperatorStatusCondition{ + {Type: configv1.OperatorAvailable, Status: configv1.ConditionTrue}, + {Type: configv1.OperatorProgressing, Status: configv1.ConditionFalse}, + {Type: configv1.OperatorDegraded, Status: configv1.ConditionFalse}, + {Type: configv1.OperatorUpgradeable, Status: configv1.ConditionTrue}, + } + + // OIDC test implementation converted to flat structure + // All sub-tests now run sequentially instead of using t.Run + t.Logf("=== Starting OIDC test suite - architecture enabled: %v ===", newExternalOIDCArchitectureEnabled) + + // ======================================================================== + // Sub-test 1: auth-config cm must not exist and gets deleted by the CAO + // if manually created when type not OIDC + // ======================================================================== + t.Logf(">>> Sub-test 1: auth-config cm must not exist when type not OIDC") + testClient.checkPreconditions(t, testCtx, typeOAuth, operatorAvailable, nil) + + _, err = testClient.kubeClient.CoreV1().ConfigMaps(managedNS).Get(testCtx, authCM, metav1.GetOptions{}) + require.True(t, errors.IsNotFound(err), "openshift-config-managed/auth-config configmap must be missing") + + // create cm + cm := v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: authCM, + Namespace: managedNS, + }, + Data: map[string]string{ + "test": "value", + }, + } + newCM, err := testClient.kubeClient.CoreV1().ConfigMaps(managedNS).Create(testCtx, &cm, metav1.CreateOptions{}) + require.NoError(t, err) + require.Equal(t, cm.Data, newCM.Data) + + // wait for CAO to delete it + var cmErr error + waitErr := wait.PollUntilContextTimeout(testCtx, 2*time.Second, 1*time.Minute, false, func(ctx context.Context) (bool, error) { + cmErr = nil + _, err := testClient.kubeClient.CoreV1().ConfigMaps(managedNS).Get(ctx, authCM, metav1.GetOptions{}) + if errors.IsNotFound(err) { + return true, nil + } + cmErr = err + return false, nil + }) + require.NoError(t, cmErr, "failed to get auth configmap: %v", cmErr) + require.NoError(t, waitErr, "failed to wait for auth configmap to get deleted: %v", err) + t.Logf("<<< Sub-test 1 PASSED: auth-config cm deletion verified") + + // ======================================================================== + // Sub-test 2: invalid CEL expression rejects auth CR admission + // ======================================================================== + t.Logf(">>> Sub-test 2: invalid CEL expression rejects auth CR admission") + celTests := []struct { + name string + specUpdate func(*configv1.AuthenticationSpec) + requireFeatureGates []configv1.FeatureGateName + }{ + { + name: "uncompilable CEL expression for uid claim mapping", + specUpdate: func(s *configv1.AuthenticationSpec) { + s.OIDCProviders[0].ClaimMappings.UID = &configv1.TokenClaimOrExpressionMapping{ + Expression: "^&*!@#^*(", + } + }, + requireFeatureGates: []configv1.FeatureGateName{features.FeatureGateExternalOIDCWithAdditionalClaimMappings}, + }, + { + name: "uncompilable CEL expression for extras claim mapping", + specUpdate: func(s *configv1.AuthenticationSpec) { + s.OIDCProviders[0].ClaimMappings.Extra = []configv1.ExtraMapping{ + { + Key: "testing/key", + ValueExpression: "^&*!@#^*(", + }, + } + }, + requireFeatureGates: []configv1.FeatureGateName{features.FeatureGateExternalOIDCWithAdditionalClaimMappings}, + }, + } + + for _, tt := range celTests { + t.Logf(" - Testing: %s", tt.name) + skipTest := false + for _, fg := range tt.requireFeatureGates { + if !featureGateEnabled(testCtx, testClient.configClient, fg) { + t.Logf(" SKIPPED: required feature gate %q is not enabled", fg) + skipTest = true + break + } + } + if skipTest { + continue + } + + _, err := testClient.updateAuthResource(t, testCtx, testSpec, tt.specUpdate) + require.Error(t, err, "uncompilable CEL expression should return in admission error") + t.Logf(" PASSED: admission rejected as expected") + } + t.Logf("<<< Sub-test 2 PASSED: invalid CEL expression tests completed") + + // ======================================================================== + // Sub-test 3: invalid OIDC config degrades auth operator + // ======================================================================== + t.Logf(">>> Sub-test 3: invalid OIDC config degrades auth operator") + invalidConfigTests := []struct { + name string + specUpdate func(*configv1.AuthenticationSpec) + requireFeatureGates []configv1.FeatureGateName + }{ + { + name: "invalid issuer CA bundle", + specUpdate: func(s *configv1.AuthenticationSpec) { + s.OIDCProviders[0].Issuer.CertificateAuthority.Name = "invalid-ca-bundle" + }, + requireFeatureGates: []configv1.FeatureGateName{}, + }, + { + name: "invalid issuer URL", + specUpdate: func(s *configv1.AuthenticationSpec) { + s.OIDCProviders[0].Issuer.URL = "https://invalid-idp.testing" + }, + requireFeatureGates: []configv1.FeatureGateName{}, + }, + } + + for _, tt := range invalidConfigTests { + t.Logf(" - Testing: %s", tt.name) + skipTest := false + for _, fg := range tt.requireFeatureGates { + if !featureGateEnabled(testCtx, testClient.configClient, fg) { + t.Logf(" SKIPPED: required feature gate %q is not enabled", fg) + skipTest = true + break + } + } + if skipTest { + continue + } + + err := testClient.authResourceRollback(testCtx, origAuthSpec) + require.NoError(t, err, "failed to roll back auth resource") + + testClient.checkPreconditions(t, testCtx, typeOAuth, operatorAvailable, nil) + + _, err = testClient.updateAuthResource(t, testCtx, testSpec, tt.specUpdate) + require.NoError(t, err, "failed to update authentication/cluster") + + require.NoError(t, test.WaitForClusterOperatorDegraded(t, testClient.configClient.ConfigV1(), "authentication")) + + testClient.validateOAuthState(t, testCtx, false, newExternalOIDCArchitectureEnabled) + t.Logf(" PASSED: operator degraded as expected") + } + t.Logf("<<< Sub-test 3 PASSED: invalid OIDC config degradation tests completed") + + // ======================================================================== + // Sub-test 4: OIDC config rolls out successfully with different + // username claim and prefix policies + // ======================================================================== + t.Logf(">>> Sub-test 4: OIDC config rolls out successfully") + err = testClient.authResourceRollback(testCtx, origAuthSpec) + require.NoError(t, err, "failed to roll back auth resource") + + usernameTests := []struct { + claim string + prefixPolicy configv1.UsernamePrefixPolicy + prefix *configv1.UsernamePrefix + expectedPrefix string + }{ + {"email", configv1.Prefix, &configv1.UsernamePrefix{PrefixString: "oidc-test:"}, "oidc-test:"}, + {"email", configv1.NoPrefix, nil, ""}, + {"sub", configv1.NoOpinion, nil, idpURL + "#"}, + {"email", configv1.NoOpinion, nil, ""}, + } + + for _, tt := range usernameTests { + policyStr := "NoOpinion" + if len(tt.prefixPolicy) > 0 { + policyStr = string(tt.prefixPolicy) + } + testName := fmt.Sprintf("username claim %s prefix policy %s", tt.claim, policyStr) + t.Logf(" - Testing: %s", testName) + + testClient.checkPreconditions(t, testCtx, nil, operatorAvailable, operatorAvailable) + + kasOriginalRevision := testClient.kasLatestAvailableRevision(t, testCtx) + auth, err := testClient.updateAuthResource(t, testCtx, testSpec, func(baseSpec *configv1.AuthenticationSpec) { + baseSpec.OIDCProviders[0].ClaimMappings.Username = configv1.UsernameClaimMapping{ + Claim: tt.claim, + PrefixPolicy: tt.prefixPolicy, + Prefix: tt.prefix, + } + }) + require.NoError(t, err, "failed to update authentication/cluster") + + require.NoError(t, test.WaitForClusterOperatorStatusAlwaysAvailable(t, testCtx, testClient.configClient.ConfigV1(), "authentication")) + + if !newExternalOIDCArchitectureEnabled { + require.NoError(t, test.WaitForClusterOperatorStatusAlwaysAvailable(t, testCtx, testClient.configClient.ConfigV1(), "kube-apiserver")) + testClient.requireKASRolloutSuccessful(t, testCtx, &auth.Spec, kasOriginalRevision, tt.expectedPrefix) + } + + testClient.validateOAuthState(t, testCtx, true, newExternalOIDCArchitectureEnabled) + + testClient.testOIDCAuthentication(t, testCtx, kcClient, tt.claim, tt.expectedPrefix, true) + t.Logf(" PASSED: rollout successful for %s", testName) + } + t.Logf("<<< Sub-test 4 PASSED: OIDC config rollout tests completed") + + // ======================================================================== + // Sub-test 5: auth-config cm must exist and gets overwritten by the CAO + // if manually modified when type OIDC + // ======================================================================== + t.Logf(">>> Sub-test 5: auth-config cm must exist and gets overwritten when type OIDC") + testClient.checkPreconditions(t, testCtx, typeOIDC, operatorAvailable, nil) + + cm2, err := testClient.kubeClient.CoreV1().ConfigMaps(managedNS).Get(testCtx, authCM, metav1.GetOptions{}) + require.NoError(t, err) + require.NotNil(t, cm2) + + orig := cm2.DeepCopy() + cm2.Data["auth-config.json"] = "manually overwritten" + cm2, err = testClient.kubeClient.CoreV1().ConfigMaps(managedNS).Update(testCtx, cm2, metav1.UpdateOptions{}) + require.NoError(t, err) + require.NotEqual(t, cm2.Data, orig.Data) + + // wait for CAO to overwrite it + var cmErr2 error + waitErr2 := wait.PollUntilContextTimeout(testCtx, 2*time.Second, 1*time.Minute, false, func(ctx context.Context) (bool, error) { + cm2, cmErr2 = testClient.kubeClient.CoreV1().ConfigMaps(managedNS).Get(ctx, authCM, metav1.GetOptions{}) + if cmErr2 != nil { + return false, nil //nolint:nilerr // retry until timeout; cmErr2 is asserted after polling + } + + return equality.Semantic.DeepEqual(cm2.Data, orig.Data), nil + }) + require.NoError(t, cmErr2, "failed to get auth configmap: %v", cmErr2) + require.NoError(t, waitErr2, "failed to wait for auth configmap to get overwritten: %v", waitErr2) + t.Logf("<<< Sub-test 5 PASSED: auth-config cm overwrite verified") + + // ======================================================================== + // Sub-test 6: OIDC config rolls out successfully but breaks authentication + // when username claim is unknown + // ======================================================================== + t.Logf(">>> Sub-test 6: OIDC config with unknown username claim breaks authentication") + testClient.checkPreconditions(t, testCtx, nil, operatorAvailable, operatorAvailable) + + kasOriginalRevision := testClient.kasLatestAvailableRevision(t, testCtx) + auth, err := testClient.updateAuthResource(t, testCtx, testSpec, func(baseSpec *configv1.AuthenticationSpec) { + baseSpec.OIDCProviders[0].ClaimMappings.Username = configv1.UsernameClaimMapping{ + Claim: "unknown", + PrefixPolicy: configv1.NoPrefix, + Prefix: nil, + } + }) + require.NoError(t, err, "failed to update authentication/cluster") + + require.NoError(t, test.WaitForClusterOperatorStatusAlwaysAvailable(t, testCtx, testClient.configClient.ConfigV1(), "authentication")) + + if !newExternalOIDCArchitectureEnabled { + require.NoError(t, test.WaitForClusterOperatorStatusAlwaysAvailable(t, testCtx, testClient.configClient.ConfigV1(), "kube-apiserver")) + testClient.requireKASRolloutSuccessful(t, testCtx, &auth.Spec, kasOriginalRevision, "") + } + + testClient.validateOAuthState(t, testCtx, true, newExternalOIDCArchitectureEnabled) + + testClient.testOIDCAuthentication(t, testCtx, kcClient, "", "", false) + t.Logf("<<< Sub-test 6 PASSED: authentication failed as expected with unknown claim") + + t.Logf("=== All OIDC sub-tests PASSED ===") +} + +// All helper functions from original file + +type oidcAuthResponse struct { + AccessToken string `json:"access_token"` + ExpiresIn int `json:"expires_in"` + RefreshToken string `json:"refresh_token"` + RefreshExpiresIn int `json:"refresh_expires_in"` + TokenType string `json:"token_type"` + IdToken string `json:"id_token"` + NotBeforePolicy int `json:"not_before_policy"` + SessionState string `json:"session_state"` + Scope string `json:"scope"` +} + +type expectedClaims struct { + jwt.RegisteredClaims + Email string `json:"email"` + Type string `json:"typ"` + Name string `json:"name"` + GivenName string `json:"given_name"` + FamilyName string `json:"family_name"` + PreferredUsername string `json:"preferred_username"` +} + +type jwks struct { + Keys []struct { + KID string `json:"kid"` + Use string `json:"use"` + KTY string `json:"kty"` + Alg string `json:"alg"` + N string `json:"n"` + E string `json:"e"` + } `json:"keys"` +} + +func fetchIssuerJWKS(ctx context.Context, issuerURL string) (*jwks, error) { + client := &http.Client{ + Timeout: 10 * time.Second, + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + }, + } + + // grab openid-configuration JSON which contains the URL of the provider's JWKS + req, err := http.NewRequestWithContext(ctx, http.MethodGet, issuerURL+"/.well-known/openid-configuration", nil) + if err != nil { + return nil, fmt.Errorf("could not build issuer OpenID well-known configuration request: %w", err) + } + resp, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("could not get issuer OpenID well-known configuration: %v", err) + } + defer resp.Body.Close() + + respBytes, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("could not parse well-known response body: %v", err) + } + + var oidcConfig struct { + JwksURL string `json:"jwks_uri"` + } + + if err := json.Unmarshal(respBytes, &oidcConfig); err != nil { + return nil, fmt.Errorf("could not unmarshal OpenID config: %v", err) + } + + // grab the provider's JWKS which contains the pubkey to verify token signatures + req, err = http.NewRequestWithContext(ctx, http.MethodGet, oidcConfig.JwksURL, nil) + if err != nil { + return nil, fmt.Errorf("could not build issuer OpenID JWKS request: %w", err) + } + resp, err = client.Do(req) + if err != nil { + return nil, fmt.Errorf("could not get issuer OpenID well-known JWKS configuration: %v", err) + } + defer resp.Body.Close() + + respBytes, err = io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("could not parse well-known JWKS response body: %v", err) + } + + var issuerJWKS jwks + if err := json.Unmarshal(respBytes, &issuerJWKS); err != nil { + return nil, fmt.Errorf("could not unmarshal JWKS: %v", err) + } + + return &issuerJWKS, nil +} + +func extractRSAPubKeyFunc(issuerJWKS *jwks) func(*jwt.Token) (any, error) { + return func(token *jwt.Token) (any, error) { + for _, key := range issuerJWKS.Keys { + if key.KID == token.Header["kid"] { + switch key.Alg { + case "RS256": + n, err := base64.RawURLEncoding.DecodeString(key.N) + if err != nil { + return nil, fmt.Errorf("could not decode N: %v", err) + } + e, err := base64.RawURLEncoding.DecodeString(key.E) + if err != nil { + return nil, fmt.Errorf("could not decode E: %v", err) + } + + pubkey := &rsa.PublicKey{ + N: new(big.Int).SetBytes(n), + E: int(new(big.Int).SetBytes(e).Int64()), + } + + return pubkey, nil + } + + return nil, fmt.Errorf("unexpected signing algorithm for key '%s': %s", key.KID, key.Alg) + } + } + + return nil, fmt.Errorf("could not find an RSA key for signing use in the provided JWKS") + } +} + +func checkFeatureGatesOrSkip(t testing.TB, ctx context.Context, configClient *configclient.Clientset, features ...configv1.FeatureGateName) { + featureGates, err := configClient.ConfigV1().FeatureGates().Get(ctx, "cluster", metav1.GetOptions{}) + require.NoError(t, err) + + if len(featureGates.Status.FeatureGates) != 1 { + // fail test if there are multiple feature gate versions (i.e. ongoing upgrade) + t.Fatalf("multiple feature gate versions detected") + return + } + + atLeastOneFeatureEnabled := false + for _, feature := range features { + for _, gate := range featureGates.Status.FeatureGates[0].Enabled { + if gate.Name == feature { + atLeastOneFeatureEnabled = true + break + } + } + + if atLeastOneFeatureEnabled { + break + } + } + + if !atLeastOneFeatureEnabled { + t.Skipf("skipping as none of the feature gates in %v are enabled", features) + } +} + +func authSpecForOIDCProvider(idpName, idpURL, caBundleName, groupsClaim string, oidcClientID configv1.TokenAudience) *configv1.AuthenticationSpec { + spec := configv1.AuthenticationSpec{ + Type: configv1.AuthenticationTypeOIDC, + WebhookTokenAuthenticator: nil, + OIDCProviders: []configv1.OIDCProvider{ + { + Name: idpName, + Issuer: configv1.TokenIssuer{ + URL: idpURL, + Audiences: []configv1.TokenAudience{oidcClientID}, + CertificateAuthority: configv1.ConfigMapNameReference{ + Name: caBundleName, + }, + }, + ClaimMappings: configv1.TokenClaimMappings{ + Username: configv1.UsernameClaimMapping{ + Claim: "email", + PrefixPolicy: configv1.Prefix, + Prefix: &configv1.UsernamePrefix{ + PrefixString: "oidc-test:", + }, + }, + Groups: configv1.PrefixedClaimMapping{ + TokenClaimMapping: configv1.TokenClaimMapping{ + Claim: groupsClaim, + }, + }, + }, + }, + }, + } + + return &spec +} + +type testClient struct { + kubeConfig *rest.Config + kubeClient *kubernetes.Clientset + configClient *configclient.Clientset + operatorClient v1helpers.OperatorClient + operatorConfigClient *operatorversionedclient.Clientset + oauthClient oauthclient.Interface + routeClient routeclient.Interface + apiregistrationClient apiregistrationclient.Interface +} + +func newTestClient(t testing.TB, ctx context.Context) (*testClient, error) { + tc := &testClient{ + kubeConfig: test.NewClientConfigForTest(t), + } + + var err error + tc.kubeClient, err = kubernetes.NewForConfig(tc.kubeConfig) + if err != nil { + return nil, err + } + + tc.configClient, err = configclient.NewForConfig(tc.kubeConfig) + if err != nil { + return nil, err + } + + tc.operatorConfigClient, err = operatorversionedclient.NewForConfig(tc.kubeConfig) + if err != nil { + return nil, err + } + + tc.oauthClient, err = oauthclient.NewForConfig(tc.kubeConfig) + if err != nil { + return nil, err + } + + tc.routeClient, err = routeclient.NewForConfig(tc.kubeConfig) + if err != nil { + return nil, err + } + + tc.apiregistrationClient, err = apiregistrationclient.NewForConfig(tc.kubeConfig) + if err != nil { + return nil, err + } + + var dynamicInformers dynamicinformer.DynamicSharedInformerFactory + tc.operatorClient, dynamicInformers, err = genericoperatorclient.NewClusterScopedOperatorClient( + clock.RealClock{}, + tc.kubeConfig, + operatorv1.GroupVersion.WithResource("authentications"), + operatorv1.GroupVersion.WithKind("Authentication"), + operator.ExtractOperatorSpec, + operator.ExtractOperatorStatus, + ) + if err != nil { + return nil, err + } + + dynamicInformers.Start(ctx.Done()) + dynamicInformers.WaitForCacheSync(ctx.Done()) + + return tc, nil +} + +func (tc *testClient) getAuth(t testing.TB, ctx context.Context) *configv1.Authentication { + auth, err := tc.configClient.ConfigV1().Authentications().Get(ctx, "cluster", metav1.GetOptions{}) + require.NoError(t, err, "failed to get authentication/cluster") + require.NotNil(t, auth) + + return auth +} + +// updateAuthResource deep-copies the baseSpec, applies updates to the copy and persists them in the auth resource +func (tc *testClient) updateAuthResource(t testing.TB, ctx context.Context, baseSpec *configv1.AuthenticationSpec, updateAuthSpec func(baseSpec *configv1.AuthenticationSpec)) (*configv1.Authentication, error) { + auth := tc.getAuth(t, ctx) + if updateAuthSpec == nil { + return auth, nil + } + + spec := baseSpec.DeepCopy() + updateAuthSpec(spec) + + // Log the authentication type change for debugging + t.Logf("Updating authentication resource: type %q -> %q", auth.Spec.Type, spec.Type) + if len(spec.OIDCProviders) > 0 { + t.Logf(" OIDC providers: %d", len(spec.OIDCProviders)) + } + if spec.WebhookTokenAuthenticator != nil { + t.Logf(" Webhook authenticator: %s", spec.WebhookTokenAuthenticator.KubeConfig.Name) + } + + auth.Spec = *spec + auth, err := tc.configClient.ConfigV1().Authentications().Update(ctx, auth, metav1.UpdateOptions{}) + if err != nil { + return nil, err + } + + require.True(t, equality.Semantic.DeepEqual(auth.Spec, *spec), + "authentication spec mismatch after update - expected type=%q, got type=%q", + spec.Type, auth.Spec.Type) + + // Verify the update persisted by reading it back + updatedAuth := tc.getAuth(t, ctx) + if updatedAuth == nil { + return nil, fmt.Errorf("failed to verify update - auth object is nil") + } + require.Equal(t, spec.Type, updatedAuth.Spec.Type, + "authentication type changed after update - expected %q, got %q (possible webhook or controller reset)", + spec.Type, updatedAuth.Spec.Type) + t.Logf("Confirmed authentication resource updated successfully with type=%q", updatedAuth.Spec.Type) + + return auth, nil +} + +func (tc *testClient) checkPreconditions(t testing.TB, ctx context.Context, authType *configv1.AuthenticationType, caoStatus []configv1.ClusterOperatorStatusCondition, kasoStatus []configv1.ClusterOperatorStatusCondition) { + var preconditionErr error + waitErr := wait.PollUntilContextTimeout(ctx, 30*time.Second, 20*time.Minute, false, func(ctx context.Context) (bool, error) { + preconditionErr = nil + if authType != nil { + expected := *authType + if len(expected) == 0 { + expected = configv1.AuthenticationTypeIntegratedOAuth + } + + auth := tc.getAuth(t, ctx) + actual := auth.Spec.Type + if len(actual) == 0 { + actual = configv1.AuthenticationTypeIntegratedOAuth + } + + if expected != actual { + preconditionErr = fmt.Errorf("unexpected auth type; test requires '%s', but got '%s'", expected, actual) + return false, nil + } + } + + if len(caoStatus) > 0 { + ok, conditions, err := test.CheckClusterOperatorStatus(t, ctx, tc.configClient.ConfigV1(), "authentication", caoStatus...) + if err != nil { + preconditionErr = fmt.Errorf("could not determine authentication operator status: %v", err) + return false, nil + } else if !ok { + preconditionErr = fmt.Errorf("unexpected authentication operator status: %v", conditions) + return false, nil + } + } + + if len(kasoStatus) > 0 { + ok, conditions, err := test.CheckClusterOperatorStatus(t, ctx, tc.configClient.ConfigV1(), "kube-apiserver", kasoStatus...) + if err != nil { + preconditionErr = fmt.Errorf("could not determine kube-apiserver operator status: %v", err) + return false, nil + } else if !ok { + preconditionErr = fmt.Errorf("unexpected kube-apiserver operator status: %v", conditions) + return false, nil + } + } + + return true, nil + }) + + require.NoError(t, preconditionErr, "failed to assert preconditions: %v", preconditionErr) + require.NoError(t, waitErr, "failed to wait for test preconditions: %v", waitErr) +} + +func (tc *testClient) kasLatestAvailableRevision(t testing.TB, ctx context.Context) int32 { + kas, err := tc.operatorConfigClient.OperatorV1().KubeAPIServers().Get(ctx, "cluster", metav1.GetOptions{}) + require.NoError(t, err, "failed to get kubeapiserver/cluster") + return kas.Status.LatestAvailableRevision +} + +func (tc *testClient) validateKASConfig(t testing.TB, ctx context.Context) int32 { + kas, err := tc.operatorConfigClient.OperatorV1().KubeAPIServers().Get(ctx, "cluster", metav1.GetOptions{}) + require.NoError(t, err) + + var observedConfig map[string]any + err = json.Unmarshal(kas.Spec.ObservedConfig.Raw, &observedConfig) + require.NoError(t, err) + + apiServerArgs, ok := observedConfig["apiServerArguments"].(map[string]any) + if !ok { + require.FailNow(t, "apiServerArguments not found or wrong type") + } + + require.Nil(t, apiServerArgs["authentication-token-webhook-config-file"]) + require.Nil(t, apiServerArgs["authentication-token-webhook-version"]) + require.Nil(t, observedConfig["authConfig"]) + + authConfigArg, ok := apiServerArgs["authentication-config"].([]any) + if !ok { + require.FailNow(t, "authentication-config not found or wrong type") + } + require.NotEmpty(t, authConfigArg) + authConfigPath, ok := authConfigArg[0].(string) + if !ok { + require.FailNow(t, "authentication-config[0] is not a string") + } + require.Equal(t, authConfigPath, "/etc/kubernetes/static-pod-resources/configmaps/auth-config/auth-config.json") + + return kas.Status.LatestAvailableRevision +} + +func (tc *testClient) validateAuthConfigJSON(t testing.TB, ctx context.Context, authSpec *configv1.AuthenticationSpec, usernamePrefix, groupsClaim, groupsPrefix string, kasRevision int32) { + idpURL := authSpec.OIDCProviders[0].Issuer.URL + caBundleName := authSpec.OIDCProviders[0].Issuer.CertificateAuthority.Name + certData := "" + if len(caBundleName) > 0 { + cm, err := tc.kubeClient.CoreV1().ConfigMaps("openshift-config").Get(ctx, caBundleName, metav1.GetOptions{}) + require.NoError(t, err) + certData = cm.Data["ca-bundle.crt"] + } + + authConfigJSONTemplate := `{"kind":"AuthenticationConfiguration","apiVersion":"apiserver.config.k8s.io/v1beta1","jwt":[{"issuer":{"url":"%s","certificateAuthority":"%s","audiences":[%s],"audienceMatchPolicy":"MatchAny"},"claimMappings":{"username":{"claim":"%s","prefix":"%s"},"groups":{"claim":"%s","prefix":"%s"},"uid":{}}}]}` + // If the ExternalOIDCWithUIDAndExtraClaimMappings feature gate is enabled, default the uid claim to "sub" + if featureGateEnabled(ctx, tc.configClient, features.FeatureGateExternalOIDCWithAdditionalClaimMappings) { + authConfigJSONTemplate = `{"kind":"AuthenticationConfiguration","apiVersion":"apiserver.config.k8s.io/v1beta1","jwt":[{"issuer":{"url":"%s","certificateAuthority":"%s","audiences":[%s],"audienceMatchPolicy":"MatchAny"},"claimMappings":{"username":{"claim":"%s","prefix":"%s"},"groups":{"claim":"%s","prefix":"%s"},"uid":{"claim":"sub"}}}]}` + } + + expectedAuthConfigJSON := fmt.Sprintf(authConfigJSONTemplate, + idpURL, + strings.ReplaceAll(certData, "\n", "\\n"), + strings.Join([]string{fmt.Sprintf(`"%s"`, oidcClientId)}, ","), + authSpec.OIDCProviders[0].ClaimMappings.Username.Claim, + usernamePrefix, + groupsClaim, + groupsPrefix, + ) + + for _, cm := range []struct { + ns string + name string + }{ + {managedNS, authCM}, + {"openshift-kube-apiserver", authCM}, + {"openshift-kube-apiserver", fmt.Sprintf("%s-%d", authCM, kasRevision)}, + } { + actualCM, err := tc.kubeClient.CoreV1().ConfigMaps(cm.ns).Get(ctx, cm.name, metav1.GetOptions{}) + require.NoError(t, err) + require.Equal(t, expectedAuthConfigJSON, actualCM.Data["auth-config.json"], "unexpected auth-config.json contents in %s/%s", actualCM.Namespace, actualCM.Name) + } +} + +func (tc *testClient) validateOAuthState(t testing.TB, ctx context.Context, requireMissing, newExternalOIDCArchitectureEnabled bool) { + dynamicClient, err := dynamic.NewForConfig(tc.kubeConfig) + require.NoError(t, err, "unexpected error while creating dynamic client") + + // Verify the authentication resource has the expected type before validating OAuth state + auth := tc.getAuth(t, ctx) + if requireMissing { + // When OAuth resources should be missing, we expect type=OIDC (or webhook for new arch) + expectedType := configv1.AuthenticationTypeOIDC + if newExternalOIDCArchitectureEnabled { + if auth.Spec.WebhookTokenAuthenticators != nil && len(auth.Spec.WebhookTokenAuthenticators) > 0 { + expectedType = "" + } + } + actualType := auth.Spec.Type + if actualType != expectedType { + t.Logf("WARNING: Authentication type mismatch before validation - expected %q, got %q", expectedType, actualType) + t.Logf("Full authentication spec: %+v", auth.Spec) + } + } + + var validationErrs []error + // Increased timeout from 5 minutes to 10 minutes to allow for slower OAuth resource cleanup + waitErr := wait.PollUntilContextTimeout(ctx, 30*time.Second, 10*time.Minute, false, func(pollCtx context.Context) (bool, error) { + validationErrs = make([]error, 0) + validationErrs = append(validationErrs, validateOAuthResources(pollCtx, dynamicClient, requireMissing, newExternalOIDCArchitectureEnabled)...) + validationErrs = append(validationErrs, validateOAuthRoutes(pollCtx, tc.routeClient, tc.configClient, requireMissing)...) + validationErrs = append(validationErrs, validateOAuthControllerConditions(tc.operatorClient, requireMissing, newExternalOIDCArchitectureEnabled)...) + validationErrs = append(validationErrs, validateOperandVersions(pollCtx, tc.configClient, requireMissing, newExternalOIDCArchitectureEnabled)...) + validationErrs = append(validationErrs, validateOAuthRelatedObjects(pollCtx, tc.configClient, requireMissing)...) + + // Log progress every iteration to help debug timeout issues + if len(validationErrs) > 0 { + t.Logf("OAuth state validation in progress, %d validation errors remaining", len(validationErrs)) + } + + return len(validationErrs) == 0, nil + }) + + require.NoError(t, utilerrors.NewAggregate(validationErrs), "failed to validate OAuth state") + require.NoError(t, waitErr, "failed to wait for OAuth state validation") +} + +func validateOAuthResources(ctx context.Context, dynamicClient *dynamic.DynamicClient, requireMissing, newExternalOIDCArchitectureEnabled bool) []error { + errs := make([]error, 0) + + type resourceReference struct { + gvr schema.GroupVersionResource + namespace string + name string + } + + resourceReferences := []resourceReference{ + {schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"}, "openshift-authentication", "v4-0-config-system-cliconfig"}, + {schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"}, "openshift-authentication", "v4-0-config-system-metadata"}, + {schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"}, "openshift-authentication", "v4-0-config-system-service-ca"}, + {schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"}, "openshift-authentication", "v4-0-config-system-trusted-ca-bundle"}, + {schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"}, "openshift-config-managed", "oauth-serving-cert"}, + {schema.GroupVersionResource{Group: "", Version: "v1", Resource: "secrets"}, "openshift-authentication", "v4-0-config-system-ocp-branding-template"}, + {schema.GroupVersionResource{Group: "", Version: "v1", Resource: "secrets"}, "openshift-authentication", "v4-0-config-system-session"}, + {schema.GroupVersionResource{Group: "", Version: "v1", Resource: "serviceaccounts"}, "openshift-authentication", "oauth-openshift"}, + {schema.GroupVersionResource{Group: "", Version: "v1", Resource: "services"}, "openshift-authentication", "oauth-openshift"}, + {schema.GroupVersionResource{Group: "apiregistration.k8s.io", Version: "v1", Resource: "apiservices"}, "", "v1.oauth.openshift.io"}, + {schema.GroupVersionResource{Group: "apiregistration.k8s.io", Version: "v1", Resource: "apiservices"}, "", "v1.user.openshift.io"}, + {schema.GroupVersionResource{Group: "oauth.openshift.io", Version: "v1", Resource: "oauthclients"}, "", "openshift-browser-client"}, + {schema.GroupVersionResource{Group: "oauth.openshift.io", Version: "v1", Resource: "oauthclients"}, "", "openshift-challenging-client"}, + {schema.GroupVersionResource{Group: "oauth.openshift.io", Version: "v1", Resource: "oauthclients"}, "", "openshift-cli-client"}, + {schema.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "clusterrolebindings"}, "", "system:openshift:openshift-authentication"}, + {schema.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "clusterrolebindings"}, "", "system:openshift:useroauthaccesstoken-manager"}, + {schema.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "clusterroles"}, "", "system:openshift:useroauthaccesstoken-manager"}, + {schema.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "rolebindings"}, "openshift-config-managed", "system:openshift:oauth-servercert-trust"}, + {schema.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "roles"}, "openshift-config-managed", "system:openshift:oauth-servercert-trust"}, + } + + // when running under the new architecture, the following resources should + // always be present and shouldn't be included in our checks + if !newExternalOIDCArchitectureEnabled { + resourceReferences = append(resourceReferences, + resourceReference{schema.GroupVersionResource{Group: "", Version: "v1", Resource: "secrets"}, "openshift-config", "webhook-authentication-integrated-oauth"}, + resourceReference{schema.GroupVersionResource{Group: "", Version: "v1", Resource: "services"}, "openshift-oauth-apiserver", "api"}, + resourceReference{schema.GroupVersionResource{Group: "", Version: "v1", Resource: "serviceaccounts"}, "openshift-oauth-apiserver", "oauth-apiserver-sa"}, + resourceReference{schema.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "clusterrolebindings"}, "", "system:openshift:oauth-apiserver"}, + ) + } + + for _, obj := range resourceReferences { + _, err := dynamicClient.Resource(obj.gvr).Namespace(obj.namespace).Get(ctx, obj.name, metav1.GetOptions{}) + if err != nil && !errors.IsNotFound(err) { + errs = append(errs, fmt.Errorf("unexpected error while getting resource %s/%s: %v", obj.namespace, obj.name, err)) + } else if requireMissing != errors.IsNotFound(err) { + errs = append(errs, fmt.Errorf("resource %s '%s/%s' wanted missing: %v; got: %v (error: %v)", obj.gvr.String(), obj.namespace, obj.name, requireMissing, errors.IsNotFound(err), err)) + } + } + + return errs +} + +func validateOAuthRoutes(ctx context.Context, routeClient routeclient.Interface, configClient *configclient.Clientset, requireMissing bool) []error { + errs := make([]error, 0) + for _, obj := range []struct{ namespace, name string }{ + {"openshift-authentication", "oauth-openshift"}, + } { + _, err := routeClient.RouteV1().Routes(obj.namespace).Get(ctx, obj.name, metav1.GetOptions{}) + if err != nil && !errors.IsNotFound(err) { + errs = append(errs, fmt.Errorf("unexpected error while getting route %s/%s: %v", obj.namespace, obj.name, err)) + } else if requireMissing != errors.IsNotFound(err) { + errs = append(errs, fmt.Errorf("route %s/%s wanted missing: %v; got missing: %v", obj.namespace, obj.name, requireMissing, errors.IsNotFound(err))) + } + + // ingress status + ingress, err := configClient.ConfigV1().Ingresses().Get(ctx, "cluster", metav1.GetOptions{}) + if err != nil { + return append(errs, err) + } + + found := false + for _, route := range ingress.Status.ComponentRoutes { + if route.Name == obj.name && route.Namespace == obj.namespace { + found = true + break + } + } + + if !requireMissing && !found { + errs = append(errs, fmt.Errorf("route %s required but was not found", obj)) + } else if requireMissing && found { + errs = append(errs, fmt.Errorf("route %s required to be missing but was found", obj)) + } + } + + return errs +} + +func validateOAuthControllerConditions(operatorClient v1helpers.OperatorClient, requireMissing, newExternalOIDCArchitectureEnabled bool) []error { + errs := make([]error, 0) + controllerConditionTypes := sets.New[string]( + // endpointAccessibleController + "OAuthServerRouteEndpointAccessibleControllerAvailable", + "OAuthServerServiceEndpointAccessibleControllerAvailable", + "OAuthServerServiceEndpointsEndpointAccessibleControllerAvailable", + // payloadConfigController + "OAuthConfigDegraded", + "OAuthSessionSecretDegraded", + "OAuthConfigRouteDegraded", + "OAuthConfigIngressDegraded", + "OAuthConfigServiceDegraded", + // ingressNodesAvailableController + "ReadyIngressNodesAvailable", + // ingressStateController + "IngressStateEndpointsDegraded", + "IngressStatePodsDegraded", + // metadataController + "IngressConfigDegraded", + "AuthConfigDegraded", + "OAuthSystemMetadataDegraded", + // routerCertsDomainValidationController + "RouterCertsDegraded", + // serviceCAController + "OAuthServiceDegraded", + "SystemServiceCAConfigDegraded", + // wellKnownReadyController + "WellKnownAvailable", + "WellKnownReadyControllerDegradationObserved", + ) + + if !newExternalOIDCArchitectureEnabled { + // webhookAuthenticatorController + controllerConditionTypes.Insert("AuthenticatorCertKeyProgressing") + } + + _, operatorStatus, _, err := operatorClient.GetOperatorState() + if err != nil { + return append(errs, err) + } + + allConditions := sets.New[string]() + for _, condition := range operatorStatus.Conditions { + allConditions.Insert(condition.Type) + } + + if requireMissing { + // no controller conditions must exist in operator status + if intersection := controllerConditionTypes.Intersection(allConditions); intersection.Len() > 0 { + return append(errs, fmt.Errorf("expected conditions to be missing but were found: %v", intersection.UnsortedList())) + } + return nil + } + + if diff := controllerConditionTypes.Difference(allConditions); diff.Len() > 0 { + // all controller conditions must exist in operator status + return append(errs, fmt.Errorf("expected conditions to exist, but were not found: %v", diff.UnsortedList())) + } + + return nil +} + +func validateOperandVersions(ctx context.Context, cfgClient *configclient.Clientset, requireMissing, newExternalOIDCArchitectureEnabled bool) []error { + operands := sets.New("oauth-openshift") + + // If the new architecture for external OIDC is not enabled, + // test this as we always did and ensure that the oauth-apiserver + // operand version gets removed appropriately. + if !newExternalOIDCArchitectureEnabled { + operands.Insert("oauth-apiserver") + } + + authnClusterOperator, err := cfgClient.ConfigV1().ClusterOperators().Get(ctx, "authentication", metav1.GetOptions{}) + if err != nil { + return []error{fmt.Errorf("fetching authentication ClusterOperator: %w", err)} + } + + foundOperands := []string{} + for _, version := range authnClusterOperator.Status.Versions { + if operands.Has(version.Name) { + foundOperands = append(foundOperands, version.Name) + } + } + + if requireMissing && len(foundOperands) > 0 { + return []error{fmt.Errorf("authentication ClusterOperator status has operands %v in versions when they should be unset", foundOperands)} + } + + foundSet := sets.New(foundOperands...) + if !requireMissing && !foundSet.Equal(operands) { + return []error{fmt.Errorf("authentication ClusterOperator status expected to have operands %v in versions but got %v", operands.UnsortedList(), foundOperands)} + } + + // If the new architecture for external OIDC is enabled, + // ensure the oauth-apiserver operand is always present. + if newExternalOIDCArchitectureEnabled { + found := false + for _, version := range authnClusterOperator.Status.Versions { + if version.Name == "oauth-apiserver" { + found = true + break + } + } + + if !found { + return []error{fmt.Errorf("authentication ClusterOperator status expected to have operand \"oauth-apiserver\" in versions but it was missing")} + } + } + + return nil +} + +func validateOAuthRelatedObjects(ctx context.Context, configClient *configclient.Clientset, requireMissing bool) []error { + co, err := configClient.ConfigV1().ClusterOperators().Get(ctx, "authentication", metav1.GetOptions{}) + if err != nil { + return []error{err} + } + + oauthRelatedObjects := []configv1.ObjectReference{ + {Group: routev1.GroupName, Resource: "routes", Name: "oauth-openshift", Namespace: "openshift-authentication"}, + {Resource: "services", Name: "oauth-openshift", Namespace: "openshift-authentication"}, + } + + errs := make([]error, 0) + for _, oauthObj := range oauthRelatedObjects { + found := false + for _, existingObj := range co.Status.RelatedObjects { + if oauthObj.Group == existingObj.Group && + oauthObj.Resource == existingObj.Resource && + oauthObj.Name == existingObj.Name && + oauthObj.Namespace == existingObj.Namespace { + found = true + break + } + } + + if requireMissing && found { + errs = append(errs, fmt.Errorf("oauth related object %s/%s %s/%s should be missing but was found in RelatedObjects", + oauthObj.Group, oauthObj.Resource, oauthObj.Namespace, oauthObj.Name)) + } else if !requireMissing && !found { + errs = append(errs, fmt.Errorf("oauth related object %s/%s %s/%s should be present but was not found in RelatedObjects", + oauthObj.Group, oauthObj.Resource, oauthObj.Namespace, oauthObj.Name)) + } + } + + return errs +} + +func (tc *testClient) testOIDCAuthentication(t testing.TB, ctx context.Context, kcClient *test.KeycloakClient, usernameClaim, usernamePrefix string, expectAuthSuccess bool) { + // re-authenticate to ensure we always have a fresh token + var err error + waitErr := wait.PollUntilContextTimeout(ctx, 5*time.Second, 30*time.Second, true, func(ctx context.Context) (bool, error) { + err = kcClient.AuthenticatePassword(oidcClientId, "", "admin", "password") + return err == nil, nil + }) + require.NoError(t, err, "failed to authenticate to keycloak: %v", err) + require.NoError(t, waitErr, "failed to wait for keycloak authentication: %v", waitErr) + + group := names.SimpleNameGenerator.GenerateName("e2e-keycloak-group-") + err = kcClient.CreateGroup(group) + require.NoError(t, err) + + user := names.SimpleNameGenerator.GenerateName("e2e-keycloak-user-") + email := fmt.Sprintf("%s@test.dev", user) + password := "password" + firstName := "Homer" + lastName := "Simpson" + err = kcClient.CreateUser( + user, + email, + password, + []string{group}, + map[string]string{ + "firstName": firstName, + "lastName": lastName, + }, + ) + require.NoError(t, err) + + // use a keycloak client for the user created above to fetch its tokens + transport, err := rest.TransportFor(tc.kubeConfig) + require.NoError(t, err) + userClient := test.KeycloakClientFor(t, transport, kcClient.IssuerURL(), "master") + err = userClient.AuthenticatePassword(oidcClientId, "", user, password) + require.NoError(t, err) + accessTokenStr, idTokenStr := userClient.Tokens() + require.NotEmpty(t, accessTokenStr, "access token must not be empty") + require.NotEmpty(t, idTokenStr, "id token must not be empty") + + // fetch issuer's JWKS and use it to parse JWT tokens + issuerJWKS, err := fetchIssuerJWKS(ctx, kcClient.IssuerURL()) + require.NoError(t, err) + require.NotNil(t, issuerJWKS) + keyfunc := extractRSAPubKeyFunc(issuerJWKS) + + accessToken, err := jwt.ParseWithClaims(accessTokenStr, &expectedClaims{}, keyfunc) + require.NoError(t, err) + require.NotNil(t, accessToken) + + idToken, err := jwt.ParseWithClaims(idTokenStr, &expectedClaims{}, keyfunc) + require.NoError(t, err) + require.NotNil(t, idToken) + + // validate the contents of the OIDC tokens + actualAccessTokenClaims := accessToken.Claims.(*expectedClaims) + require.True(t, accessToken.Valid) + require.Equal(t, userClient.IssuerURL(), actualAccessTokenClaims.Issuer) + require.Equal(t, user, actualAccessTokenClaims.PreferredUsername) + require.Equal(t, email, actualAccessTokenClaims.Email) + require.Equal(t, "Bearer", actualAccessTokenClaims.Type) + require.Equal(t, firstName, actualAccessTokenClaims.GivenName) + require.Equal(t, lastName, actualAccessTokenClaims.FamilyName) + require.Equal(t, fmt.Sprintf("%s %s", firstName, lastName), actualAccessTokenClaims.Name) + require.NotEmpty(t, actualAccessTokenClaims.Subject) + + actualIDTokenClaims := idToken.Claims.(*expectedClaims) + require.True(t, idToken.Valid) + require.Equal(t, userClient.IssuerURL(), actualIDTokenClaims.Issuer) + require.Equal(t, user, actualIDTokenClaims.PreferredUsername) + require.Equal(t, email, actualIDTokenClaims.Email) + require.Equal(t, "ID", actualIDTokenClaims.Type) + require.Equal(t, jwt.ClaimStrings{oidcClientId}, actualIDTokenClaims.Audience) + require.Equal(t, firstName, actualIDTokenClaims.GivenName) + require.Equal(t, lastName, actualIDTokenClaims.FamilyName) + require.Equal(t, fmt.Sprintf("%s %s", firstName, lastName), actualIDTokenClaims.Name) + require.NotEmpty(t, actualIDTokenClaims.Subject) + + // test authentication via the kube-apiserver + // create a new kube client that uses the OIDC id_token as a bearer token + kubeConfig := rest.AnonymousClientConfig(tc.kubeConfig) + kubeConfig.BearerToken = idTokenStr + kubeClient, err := kubernetes.NewForConfig(kubeConfig) + require.NoError(t, err) + + ssr, err := kubeClient.AuthenticationV1().SelfSubjectReviews().Create(ctx, &authenticationv1.SelfSubjectReview{}, metav1.CreateOptions{}) + if expectAuthSuccess { + // test authentication with the OIDC token using a self subject review + expectedUsername := "" + switch usernameClaim { + case "email": + expectedUsername = usernamePrefix + email + case "sub": + expectedUsername = usernamePrefix + actualIDTokenClaims.Subject + default: + t.Fatalf("unexpected username claim: %s", usernameClaim) + } + + require.NoError(t, err) + require.NotNil(t, ssr) + require.Contains(t, ssr.Status.UserInfo.Groups, "system:authenticated") + require.Equal(t, expectedUsername, ssr.Status.UserInfo.Username) + } else { + require.Error(t, err) + require.True(t, errors.IsUnauthorized(err)) + } +} + +func (tc *testClient) requireKASRolloutSuccessful(t testing.TB, testCtx context.Context, authSpec *configv1.AuthenticationSpec, kasOriginalRevision int32, expectedUsernamePrefix string) { + // wait for KAS rollout + err := test.WaitForNewKASRollout(t, testCtx, tc.operatorConfigClient.OperatorV1().KubeAPIServers(), kasOriginalRevision) + require.NoError(t, err, "failed to wait for KAS rollout") + + kasRevision := tc.validateKASConfig(t, testCtx) + tc.validateAuthConfigJSON(t, testCtx, authSpec, expectedUsernamePrefix, oidcGroupsClaim, oidcGroupsPrefix, kasRevision) +} + +func (tc *testClient) authResourceRollback(ctx context.Context, origAuthSpec *configv1.AuthenticationSpec) error { + auth, err := tc.configClient.ConfigV1().Authentications().Get(ctx, "cluster", metav1.GetOptions{}) + if err != nil { + return fmt.Errorf("rollback failed for authentication/cluster while retrieving fresh object: %w", err) + } + + if !equality.Semantic.DeepEqual(auth.Spec, *origAuthSpec) { + auth.Spec = *origAuthSpec + if _, err := tc.configClient.ConfigV1().Authentications().Update(ctx, auth, metav1.UpdateOptions{}); err != nil { + return fmt.Errorf("rollback failed for authentication '%s' while updating object: %w", auth.Name, err) + } + } + + return nil +} + +func featureGateEnabled(ctx context.Context, configClient *configclient.Clientset, feature configv1.FeatureGateName) bool { + featureGates, err := configClient.ConfigV1().FeatureGates().Get(ctx, "cluster", metav1.GetOptions{}) + if err != nil { + return false + } + + if len(featureGates.Status.FeatureGates) == 0 { + return false + } + + for _, enabled := range featureGates.Status.FeatureGates[0].Enabled { + if enabled.Name == feature { + return true + } + } + + return false +} diff --git a/test/e2e-oidc/external_oidc_test.go b/test/e2e-oidc/external_oidc_test.go index 298b12b94a..20681e9e04 100644 --- a/test/e2e-oidc/external_oidc_test.go +++ b/test/e2e-oidc/external_oidc_test.go @@ -1,1141 +1,14 @@ -package e2e +package e2e_oidc import ( - "context" - "crypto/rsa" - "crypto/tls" - "encoding/base64" - "encoding/json" - "fmt" - "io" - "math/big" - "net/http" - "strings" "testing" - "time" - - configv1 "github.com/openshift/api/config/v1" - "github.com/openshift/api/features" - operatorv1 "github.com/openshift/api/operator/v1" - routev1 "github.com/openshift/api/route/v1" - configclient "github.com/openshift/client-go/config/clientset/versioned" - oauthclient "github.com/openshift/client-go/oauth/clientset/versioned" - operatorversionedclient "github.com/openshift/client-go/operator/clientset/versioned" - routeclient "github.com/openshift/client-go/route/clientset/versioned" - "github.com/openshift/cluster-authentication-operator/pkg/operator" - test "github.com/openshift/cluster-authentication-operator/test/library" - "github.com/openshift/library-go/pkg/operator/genericoperatorclient" - "github.com/openshift/library-go/pkg/operator/v1helpers" - "github.com/stretchr/testify/require" - utilerrors "k8s.io/apimachinery/pkg/util/errors" - apiregistrationclient "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset" - "k8s.io/utils/clock" - "k8s.io/utils/ptr" - - authenticationv1 "k8s.io/api/authentication/v1" - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/equality" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/util/sets" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/apiserver/pkg/storage/names" - "k8s.io/client-go/dynamic" - "k8s.io/client-go/dynamic/dynamicinformer" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" - - "github.com/golang-jwt/jwt/v5" -) - -const ( - oidcClientId = "admin-cli" - oidcGroupsClaim = "groups" - oidcGroupsPrefix = "" - - managedNS = "openshift-config-managed" - authCM = "auth-config" ) -func TestExternalOIDCWithKeycloak(t *testing.T) { - testCtx := t.Context() - testClient, err := newTestClient(t, testCtx) - require.NoError(t, err) - - checkFeatureGatesOrSkip(t, testCtx, testClient.configClient, features.FeatureGateExternalOIDC, features.FeatureGateExternalOIDCWithAdditionalClaimMappings) - - newExternalOIDCArchitectureEnabled := featureGateEnabled(testCtx, testClient.configClient, features.FeatureGateExternalOIDCExternalClaimsSourcing) - - // post-test cluster cleanup - var cleanups []func() - defer test.IDPCleanupWrapper(func() { - t.Logf("cleaning up after test") - ts := time.Now() - for _, c := range cleanups { - c() - } - t.Logf("cleanup completed after %s", time.Since(ts)) - })() - - origAuthSpec := (*testClient.getAuth(t, testCtx)).Spec.DeepCopy() - cleanups = append(cleanups, func() { - kasOriginalRevision := testClient.kasLatestAvailableRevision(t, testCtx) - - err := testClient.authResourceRollback(testCtx, origAuthSpec) - require.NoError(t, err, "failed to rollback auth resource during cleanup") - - // KAS should not need to perform a rollout when the new architecture is enabled. - // The oauth-apiserver will, but that should be fairly quick and handled by cluster operator - // stability checks prior to running tests. - if !newExternalOIDCArchitectureEnabled { - err = test.WaitForNewKASRollout(t, testCtx, testClient.operatorConfigClient.OperatorV1().KubeAPIServers(), kasOriginalRevision) - require.NoError(t, err, "failed to wait for KAS rollout during cleanup") - } - - testClient.validateOAuthState(t, testCtx, false, newExternalOIDCArchitectureEnabled) - }) - - // keycloak setup - var idpName string - var kcClient *test.KeycloakClient - kcClient, idpName, c := test.AddKeycloakIDP(t, testClient.kubeConfig, true) - cleanups = append(cleanups, c...) - t.Logf("keycloak Admin URL: %s", kcClient.AdminURL()) - - // default-ingress-cert is copied to openshift-config and used as the CA for the IdP - // see test/library/idpdeployment.go:332 - caBundleName := idpName + "-ca" - idpURL := kcClient.IssuerURL() - - // run tests - - testSpec := authSpecForOIDCProvider(idpName, idpURL, caBundleName, oidcGroupsClaim, oidcClientId) - - typeOAuth := ptr.To(configv1.AuthenticationTypeIntegratedOAuth) - typeOIDC := ptr.To(configv1.AuthenticationTypeOIDC) - operatorAvailable := []configv1.ClusterOperatorStatusCondition{ - {Type: configv1.OperatorAvailable, Status: configv1.ConditionTrue}, - {Type: configv1.OperatorProgressing, Status: configv1.ConditionFalse}, - {Type: configv1.OperatorDegraded, Status: configv1.ConditionFalse}, - {Type: configv1.OperatorUpgradeable, Status: configv1.ConditionTrue}, - } - - t.Run("auth-config cm must not exist and gets deleted by the CAO if manually created when type not OIDC", func(t *testing.T) { - testClient.checkPreconditions(t, testCtx, typeOAuth, operatorAvailable, nil) - - _, err := testClient.kubeClient.CoreV1().ConfigMaps(managedNS).Get(testCtx, authCM, metav1.GetOptions{}) - require.True(t, errors.IsNotFound(err), "openshift-config-managed/auth-config configmap must be missing") - - // create cm - cm := v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: authCM, - Namespace: managedNS, - }, - Data: map[string]string{ - "test": "value", - }, - } - newCM, err := testClient.kubeClient.CoreV1().ConfigMaps(managedNS).Create(testCtx, &cm, metav1.CreateOptions{}) - require.NoError(t, err) - require.Equal(t, cm.Data, newCM.Data) - - // wait for CAO to delete it - var cmErr error - waitErr := wait.PollUntilContextTimeout(testCtx, 2*time.Second, 1*time.Minute, false, func(ctx context.Context) (bool, error) { - cmErr = nil - _, err := testClient.kubeClient.CoreV1().ConfigMaps(managedNS).Get(testCtx, authCM, metav1.GetOptions{}) - if errors.IsNotFound(err) { - return true, nil - } - cmErr = err - return false, nil - }) - require.NoError(t, cmErr, "failed to get auth configmap: %v", cmErr) - require.NoError(t, waitErr, "failed to wait for auth configmap to get deleted: %v", err) - }) - - t.Run("invalid CEL expression rejects auth CR admission", func(t *testing.T) { - for _, tt := range []struct { - name string - specUpdate func(*configv1.AuthenticationSpec) - requireFeatureGates []configv1.FeatureGateName - }{ - { - name: "uncompilable CEL expression for uid claim mapping", - specUpdate: func(s *configv1.AuthenticationSpec) { - s.OIDCProviders[0].ClaimMappings.UID = &configv1.TokenClaimOrExpressionMapping{ - Expression: "^&*!@#^*(", - } - }, - requireFeatureGates: []configv1.FeatureGateName{features.FeatureGateExternalOIDCWithAdditionalClaimMappings}, - }, - { - name: "uncompilable CEL expression for extras claim mapping", - specUpdate: func(s *configv1.AuthenticationSpec) { - s.OIDCProviders[0].ClaimMappings.Extra = []configv1.ExtraMapping{ - { - Key: "testing/key", - ValueExpression: "^&*!@#^*(", - }, - } - }, - requireFeatureGates: []configv1.FeatureGateName{features.FeatureGateExternalOIDCWithAdditionalClaimMappings}, - }, - } { - t.Run(tt.name, func(t *testing.T) { - for _, fg := range tt.requireFeatureGates { - if !featureGateEnabled(testCtx, testClient.configClient, fg) { - t.Skipf("skipping as required feature gate %q is not enabled", fg) - } - } - _, err := testClient.updateAuthResource(t, testCtx, testSpec, tt.specUpdate) - require.Error(t, err, "uncompilable CEL expression should return in admission error") - }) - } - }) - - t.Run("invalid OIDC config degrades auth operator", func(t *testing.T) { - for _, tt := range []struct { - name string - specUpdate func(*configv1.AuthenticationSpec) - requireFeatureGates []configv1.FeatureGateName - }{ - { - name: "invalid issuer CA bundle", - specUpdate: func(s *configv1.AuthenticationSpec) { - s.OIDCProviders[0].Issuer.CertificateAuthority.Name = "invalid-ca-bundle" - }, - requireFeatureGates: []configv1.FeatureGateName{}, - }, - { - name: "invalid issuer URL", - specUpdate: func(s *configv1.AuthenticationSpec) { - s.OIDCProviders[0].Issuer.URL = "https://invalid-idp.testing" - }, - requireFeatureGates: []configv1.FeatureGateName{}, - }, - } { - t.Run(tt.name, func(t *testing.T) { - for _, fg := range tt.requireFeatureGates { - if !featureGateEnabled(testCtx, testClient.configClient, fg) { - t.Skipf("skipping as required feature gate %q is not enabled", fg) - } - } - - err := testClient.authResourceRollback(testCtx, origAuthSpec) - require.NoError(t, err, "failed to roll back auth resource") - - testClient.checkPreconditions(t, testCtx, typeOAuth, operatorAvailable, nil) - - _, err = testClient.updateAuthResource(t, testCtx, testSpec, tt.specUpdate) - require.NoError(t, err, "failed to update authentication/cluster") - - require.NoError(t, test.WaitForClusterOperatorDegraded(t, testClient.configClient.ConfigV1(), "authentication")) - - testClient.validateOAuthState(t, testCtx, false, newExternalOIDCArchitectureEnabled) - }) - } - }) - - t.Run("OIDC config rolls out successfully", func(t *testing.T) { - err := testClient.authResourceRollback(testCtx, origAuthSpec) - require.NoError(t, err, "failed to roll back auth resource") - - for _, tt := range []struct { - claim string - prefixPolicy configv1.UsernamePrefixPolicy - prefix *configv1.UsernamePrefix - expectedPrefix string - }{ - {"email", configv1.Prefix, &configv1.UsernamePrefix{PrefixString: "oidc-test:"}, "oidc-test:"}, - {"email", configv1.NoPrefix, nil, ""}, - {"sub", configv1.NoOpinion, nil, idpURL + "#"}, - {"email", configv1.NoOpinion, nil, ""}, - } { - policyStr := "NoOpinion" - if len(tt.prefixPolicy) > 0 { - policyStr = string(tt.prefixPolicy) - } - testName := fmt.Sprintf("username claim %s prefix policy %s", tt.claim, policyStr) - t.Run(testName, func(t *testing.T) { - testClient.checkPreconditions(t, testCtx, nil, operatorAvailable, operatorAvailable) - - kasOriginalRevision := testClient.kasLatestAvailableRevision(t, testCtx) - auth, err := testClient.updateAuthResource(t, testCtx, testSpec, func(baseSpec *configv1.AuthenticationSpec) { - baseSpec.OIDCProviders[0].ClaimMappings.Username = configv1.UsernameClaimMapping{ - Claim: tt.claim, - PrefixPolicy: tt.prefixPolicy, - Prefix: tt.prefix, - } - }) - require.NoError(t, err, "failed to update authentication/cluster") - - require.NoError(t, test.WaitForClusterOperatorStatusAlwaysAvailable(t, testCtx, testClient.configClient.ConfigV1(), "authentication")) - - if !newExternalOIDCArchitectureEnabled { - require.NoError(t, test.WaitForClusterOperatorStatusAlwaysAvailable(t, testCtx, testClient.configClient.ConfigV1(), "kube-apiserver")) - testClient.requireKASRolloutSuccessful(t, testCtx, &auth.Spec, kasOriginalRevision, tt.expectedPrefix) - } - - testClient.validateOAuthState(t, testCtx, true, newExternalOIDCArchitectureEnabled) - - testClient.testOIDCAuthentication(t, testCtx, kcClient, tt.claim, tt.expectedPrefix, true) - }) - } - }) - - t.Run("auth-config cm must exist and gets overwritten by the CAO if manually modified when type OIDC", func(t *testing.T) { - testClient.checkPreconditions(t, testCtx, typeOIDC, operatorAvailable, nil) - - cm, err := testClient.kubeClient.CoreV1().ConfigMaps(managedNS).Get(testCtx, authCM, metav1.GetOptions{}) - require.NoError(t, err) - require.NotNil(t, cm) - - orig := cm.DeepCopy() - cm.Data["auth-config.json"] = "manually overwritten" - cm, err = testClient.kubeClient.CoreV1().ConfigMaps(managedNS).Update(testCtx, cm, metav1.UpdateOptions{}) - require.NoError(t, err) - require.NotEqual(t, cm.Data, orig.Data) - - // wait for CAO to overwrite it - var cmErr error - waitErr := wait.PollUntilContextTimeout(testCtx, 2*time.Second, 1*time.Minute, false, func(ctx context.Context) (bool, error) { - cm, cmErr = testClient.kubeClient.CoreV1().ConfigMaps(managedNS).Get(testCtx, authCM, metav1.GetOptions{}) - if err != nil { - return false, nil - } - - return equality.Semantic.DeepEqual(cm.Data, orig.Data), nil - }) - require.NoError(t, cmErr, "failed to get auth configmap: %v", err) - require.NoError(t, waitErr, "failed to wait for auth configmap to get overwritten: %v", err) - }) - - t.Run("OIDC config rolls out successfully but breaks authentication when username claim is unknown", func(t *testing.T) { - testClient.checkPreconditions(t, testCtx, nil, operatorAvailable, operatorAvailable) - - kasOriginalRevision := testClient.kasLatestAvailableRevision(t, testCtx) - auth, err := testClient.updateAuthResource(t, testCtx, testSpec, func(baseSpec *configv1.AuthenticationSpec) { - baseSpec.OIDCProviders[0].ClaimMappings.Username = configv1.UsernameClaimMapping{ - Claim: "unknown", - PrefixPolicy: configv1.NoPrefix, - Prefix: nil, - } - }) - require.NoError(t, err, "failed to update authentication/cluster") - - require.NoError(t, test.WaitForClusterOperatorStatusAlwaysAvailable(t, testCtx, testClient.configClient.ConfigV1(), "authentication")) - - if !newExternalOIDCArchitectureEnabled { - require.NoError(t, test.WaitForClusterOperatorStatusAlwaysAvailable(t, testCtx, testClient.configClient.ConfigV1(), "kube-apiserver")) - testClient.requireKASRolloutSuccessful(t, testCtx, &auth.Spec, kasOriginalRevision, "") - } - - testClient.validateOAuthState(t, testCtx, true, newExternalOIDCArchitectureEnabled) - - testClient.testOIDCAuthentication(t, testCtx, kcClient, "", "", false) - }) -} - -type oidcAuthResponse struct { - AccessToken string `json:"access_token"` - ExpiresIn int `json:"expires_in"` - RefreshToken string `json:"refresh_token"` - RefreshExpiresIn int `json:"refresh_expires_in"` - TokenType string `json:"token_type"` - IdToken string `json:"id_token"` - NotBeforePolicy int `json:"not_before_policy"` - SessionState string `json:"session_state"` - Scope string `json:"scope"` -} - -type expectedClaims struct { - jwt.RegisteredClaims - Email string `json:"email"` - Type string `json:"typ"` - Name string `json:"name"` - GivenName string `json:"given_name"` - FamilyName string `json:"family_name"` - PreferredUsername string `json:"preferred_username"` -} - -type jwks struct { - Keys []struct { - KID string `json:"kid"` - Use string `json:"use"` - KTY string `json:"kty"` - Alg string `json:"alg"` - N string `json:"n"` - E string `json:"e"` - } `json:"keys"` -} - -func fetchIssuerJWKS(issuerURL string) (*jwks, error) { - client := &http.Client{ - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: true, - }, - }, - } - - // grab openid-configuration JSON which contains the URL of the provider's JWKS - resp, err := client.Get(issuerURL + "/.well-known/openid-configuration") - if err != nil { - return nil, fmt.Errorf("could not get issuer OpenID well-known configuration: %v", err) - } - defer resp.Body.Close() - - respBytes, err := io.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("could not parse well-known response body: %v", err) - } - - var oidcConfig struct { - JwksURL string `json:"jwks_uri"` - } - - if err := json.Unmarshal(respBytes, &oidcConfig); err != nil { - return nil, fmt.Errorf("could not unmarshal OpenID config: %v", err) - } - - // grab the provider's JWKS which contains the pubkey to verify token signatures - resp, err = client.Get(oidcConfig.JwksURL) - if err != nil { - return nil, fmt.Errorf("could not get issuer OpenID well-known JWKS configuration: %v", err) - } - defer resp.Body.Close() - - respBytes, err = io.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("could not parse well-known JWKS response body: %v", err) - } - - var issuerJWKS jwks - if err := json.Unmarshal(respBytes, &issuerJWKS); err != nil { - return nil, fmt.Errorf("could not unmarshal JWKS: %v", err) - } - - return &issuerJWKS, nil -} - -func extractRSAPubKeyFunc(issuerJWKS *jwks) func(*jwt.Token) (any, error) { - return func(token *jwt.Token) (any, error) { - for _, key := range issuerJWKS.Keys { - if key.KID == token.Header["kid"] { - switch key.Alg { - case "RS256": - n, err := base64.RawURLEncoding.DecodeString(key.N) - if err != nil { - return nil, fmt.Errorf("could not decode N: %v", err) - } - e, err := base64.RawURLEncoding.DecodeString(key.E) - if err != nil { - return nil, fmt.Errorf("could not decode E: %v", err) - } - - pubkey := &rsa.PublicKey{ - N: new(big.Int).SetBytes(n), - E: int(new(big.Int).SetBytes(e).Int64()), - } - - return pubkey, nil - } - - return nil, fmt.Errorf("unexpected signing algorithm for key '%s': %s", key.KID, key.Alg) - } - } - - return nil, fmt.Errorf("could not find an RSA key for signing use in the provided JWKS") - } -} - -func checkFeatureGatesOrSkip(t *testing.T, ctx context.Context, configClient *configclient.Clientset, features ...configv1.FeatureGateName) { - featureGates, err := configClient.ConfigV1().FeatureGates().Get(ctx, "cluster", metav1.GetOptions{}) - require.NoError(t, err) - - if len(featureGates.Status.FeatureGates) != 1 { - // fail test if there are multiple feature gate versions (i.e. ongoing upgrade) - t.Fatalf("multiple feature gate versions detected") - return - } - - atLeastOneFeatureEnabled := false - for _, feature := range features { - for _, gate := range featureGates.Status.FeatureGates[0].Enabled { - if gate.Name == feature { - atLeastOneFeatureEnabled = true - break - } - } - - if atLeastOneFeatureEnabled { - break - } - } - - if !atLeastOneFeatureEnabled { - t.Skipf("skipping as none of the feature gates in %v are enabled", features) - } -} - -func authSpecForOIDCProvider(idpName, idpURL, caBundleName, groupsClaim string, oidcClientID configv1.TokenAudience) *configv1.AuthenticationSpec { - spec := configv1.AuthenticationSpec{ - Type: configv1.AuthenticationTypeOIDC, - WebhookTokenAuthenticator: nil, - OIDCProviders: []configv1.OIDCProvider{ - { - Name: idpName, - Issuer: configv1.TokenIssuer{ - URL: idpURL, - Audiences: []configv1.TokenAudience{oidcClientID}, - CertificateAuthority: configv1.ConfigMapNameReference{ - Name: caBundleName, - }, - }, - ClaimMappings: configv1.TokenClaimMappings{ - Username: configv1.UsernameClaimMapping{ - Claim: "email", - PrefixPolicy: configv1.Prefix, - Prefix: &configv1.UsernamePrefix{ - PrefixString: "oidc-test:", - }, - }, - Groups: configv1.PrefixedClaimMapping{ - TokenClaimMapping: configv1.TokenClaimMapping{ - Claim: groupsClaim, - }, - }, - }, - }, - }, - } - - return &spec -} - -type testClient struct { - kubeConfig *rest.Config - kubeClient *kubernetes.Clientset - configClient *configclient.Clientset - operatorClient v1helpers.OperatorClient - operatorConfigClient *operatorversionedclient.Clientset - oauthClient oauthclient.Interface - routeClient routeclient.Interface - apiregistrationClient apiregistrationclient.Interface -} - -func newTestClient(t *testing.T, ctx context.Context) (*testClient, error) { - tc := &testClient{ - kubeConfig: test.NewClientConfigForTest(t), - } - - var err error - tc.kubeClient, err = kubernetes.NewForConfig(tc.kubeConfig) - if err != nil { - return nil, err - } - - tc.configClient, err = configclient.NewForConfig(tc.kubeConfig) - if err != nil { - return nil, err - } - - tc.operatorConfigClient, err = operatorversionedclient.NewForConfig(tc.kubeConfig) - if err != nil { - return nil, err - } - - tc.oauthClient, err = oauthclient.NewForConfig(tc.kubeConfig) - if err != nil { - return nil, err - } - - tc.routeClient, err = routeclient.NewForConfig(tc.kubeConfig) - if err != nil { - return nil, err - } - - tc.apiregistrationClient, err = apiregistrationclient.NewForConfig(tc.kubeConfig) - if err != nil { - return nil, err - } - - var dynamicInformers dynamicinformer.DynamicSharedInformerFactory - tc.operatorClient, dynamicInformers, err = genericoperatorclient.NewClusterScopedOperatorClient( - clock.RealClock{}, - tc.kubeConfig, - operatorv1.GroupVersion.WithResource("authentications"), - operatorv1.GroupVersion.WithKind("Authentication"), - operator.ExtractOperatorSpec, - operator.ExtractOperatorStatus, - ) - if err != nil { - return nil, err - } - - dynamicInformers.Start(ctx.Done()) - dynamicInformers.WaitForCacheSync(ctx.Done()) - - return tc, nil -} - -func (tc *testClient) getAuth(t *testing.T, ctx context.Context) *configv1.Authentication { - auth, err := tc.configClient.ConfigV1().Authentications().Get(ctx, "cluster", metav1.GetOptions{}) - require.NoError(t, err, "failed to get authentication/cluster") - require.NotNil(t, auth) - - return auth -} - -// updateAuthResource deep-copies the baseSpec, applies updates to the copy and persists them in the auth resource -func (tc *testClient) updateAuthResource(t *testing.T, ctx context.Context, baseSpec *configv1.AuthenticationSpec, updateAuthSpec func(baseSpec *configv1.AuthenticationSpec)) (*configv1.Authentication, error) { - auth := tc.getAuth(t, ctx) - if updateAuthSpec == nil { - return auth, nil - } - - spec := baseSpec.DeepCopy() - updateAuthSpec(spec) - - auth.Spec = *spec - auth, err := tc.configClient.ConfigV1().Authentications().Update(ctx, auth, metav1.UpdateOptions{}) - if err != nil { - return nil, err - } - - require.True(t, equality.Semantic.DeepEqual(auth.Spec, *spec)) - - return auth, nil -} - -func (tc *testClient) checkPreconditions(t *testing.T, ctx context.Context, authType *configv1.AuthenticationType, caoStatus []configv1.ClusterOperatorStatusCondition, kasoStatus []configv1.ClusterOperatorStatusCondition) { - var preconditionErr error - waitErr := wait.PollUntilContextTimeout(ctx, 30*time.Second, 20*time.Minute, false, func(ctx context.Context) (bool, error) { - preconditionErr = nil - if authType != nil { - expected := *authType - if len(expected) == 0 { - expected = configv1.AuthenticationTypeIntegratedOAuth - } - - auth := tc.getAuth(t, ctx) - actual := auth.Spec.Type - if len(actual) == 0 { - actual = configv1.AuthenticationTypeIntegratedOAuth - } - - if expected != actual { - preconditionErr = fmt.Errorf("unexpected auth type; test requires '%s', but got '%s'", expected, actual) - return false, nil - } - } - - if len(caoStatus) > 0 { - ok, conditions, err := test.CheckClusterOperatorStatus(t, ctx, tc.configClient.ConfigV1(), "authentication", caoStatus...) - if err != nil { - preconditionErr = fmt.Errorf("could not determine authentication operator status: %v", err) - return false, nil - } else if !ok { - preconditionErr = fmt.Errorf("unexpected authentication operator status: %v", conditions) - return false, nil - } - } - - if len(kasoStatus) > 0 { - ok, conditions, err := test.CheckClusterOperatorStatus(t, ctx, tc.configClient.ConfigV1(), "kube-apiserver", kasoStatus...) - if err != nil { - preconditionErr = fmt.Errorf("could not determine kube-apiserver operator status: %v", err) - return false, nil - } else if !ok { - preconditionErr = fmt.Errorf("unexpected kube-apiserver operator status: %v", conditions) - return false, nil - } - } - - return true, nil - }) - - require.NoError(t, preconditionErr, "failed to assert preconditions: %v", preconditionErr) - require.NoError(t, waitErr, "failed to wait for test preconditions: %v", waitErr) -} - -func (tc *testClient) kasLatestAvailableRevision(t *testing.T, ctx context.Context) int32 { - kas, err := tc.operatorConfigClient.OperatorV1().KubeAPIServers().Get(ctx, "cluster", metav1.GetOptions{}) - require.NoError(t, err, "failed to get kubeapiserver/cluster") - return kas.Status.LatestAvailableRevision -} - -func (tc *testClient) validateKASConfig(t *testing.T, ctx context.Context) int32 { - kas, err := tc.operatorConfigClient.OperatorV1().KubeAPIServers().Get(ctx, "cluster", metav1.GetOptions{}) - require.NoError(t, err) - - var observedConfig map[string]any - err = json.Unmarshal(kas.Spec.ObservedConfig.Raw, &observedConfig) - require.NoError(t, err) - - apiServerArguments := observedConfig["apiServerArguments"].(map[string]any) - - require.Nil(t, apiServerArguments["authentication-token-webhook-config-file"]) - require.Nil(t, apiServerArguments["authentication-token-webhook-version"]) - require.Nil(t, observedConfig["authConfig"]) - - authConfigArg := apiServerArguments["authentication-config"].([]any) - require.NotEmpty(t, authConfigArg) - require.Equal(t, authConfigArg[0].(string), "/etc/kubernetes/static-pod-resources/configmaps/auth-config/auth-config.json") - - return kas.Status.LatestAvailableRevision -} - -func (tc *testClient) validateAuthConfigJSON(t *testing.T, ctx context.Context, authSpec *configv1.AuthenticationSpec, usernamePrefix, groupsClaim, groupsPrefix string, kasRevision int32) { - idpURL := authSpec.OIDCProviders[0].Issuer.URL - caBundleName := authSpec.OIDCProviders[0].Issuer.CertificateAuthority.Name - certData := "" - if len(caBundleName) > 0 { - cm, err := tc.kubeClient.CoreV1().ConfigMaps("openshift-config").Get(ctx, caBundleName, metav1.GetOptions{}) - require.NoError(t, err) - certData = cm.Data["ca-bundle.crt"] - } - - authConfigJSONTemplate := `{"kind":"AuthenticationConfiguration","apiVersion":"apiserver.config.k8s.io/v1beta1","jwt":[{"issuer":{"url":"%s","certificateAuthority":"%s","audiences":[%s],"audienceMatchPolicy":"MatchAny"},"claimMappings":{"username":{"claim":"%s","prefix":"%s"},"groups":{"claim":"%s","prefix":"%s"},"uid":{}}}]}` - // If the ExternalOIDCWithUIDAndExtraClaimMappings feature gate is enabled, default the uid claim to "sub" - if featureGateEnabled(ctx, tc.configClient, features.FeatureGateExternalOIDCWithAdditionalClaimMappings) { - authConfigJSONTemplate = `{"kind":"AuthenticationConfiguration","apiVersion":"apiserver.config.k8s.io/v1beta1","jwt":[{"issuer":{"url":"%s","certificateAuthority":"%s","audiences":[%s],"audienceMatchPolicy":"MatchAny"},"claimMappings":{"username":{"claim":"%s","prefix":"%s"},"groups":{"claim":"%s","prefix":"%s"},"uid":{"claim":"sub"}}}]}` - } - - expectedAuthConfigJSON := fmt.Sprintf(authConfigJSONTemplate, - idpURL, - strings.ReplaceAll(certData, "\n", "\\n"), - strings.Join([]string{fmt.Sprintf(`"%s"`, oidcClientId)}, ","), - authSpec.OIDCProviders[0].ClaimMappings.Username.Claim, - usernamePrefix, - groupsClaim, - groupsPrefix, - ) - - for _, cm := range []struct { - ns string - name string - }{ - {"openshift-config-managed", "auth-config"}, - {"openshift-kube-apiserver", "auth-config"}, - {"openshift-kube-apiserver", fmt.Sprintf("auth-config-%d", kasRevision)}, - } { - actualCM, err := tc.kubeClient.CoreV1().ConfigMaps(cm.ns).Get(ctx, cm.name, metav1.GetOptions{}) - require.NoError(t, err) - require.Equal(t, expectedAuthConfigJSON, actualCM.Data["auth-config.json"], "unexpected auth-config.json contents in %s/%s", actualCM.Namespace, actualCM.Name) - } -} - -func (tc *testClient) validateOAuthState(t *testing.T, ctx context.Context, requireMissing, newExternalOIDCArchitectureEnabled bool) { - dynamicClient, err := dynamic.NewForConfig(tc.kubeConfig) - require.NoError(t, err, "unexpected error while creating dynamic client") - - var validationErrs []error - waitErr := wait.PollUntilContextTimeout(ctx, 30*time.Second, 5*time.Minute, false, func(_ context.Context) (bool, error) { - validationErrs = make([]error, 0) - validationErrs = append(validationErrs, validateOAuthResources(ctx, dynamicClient, requireMissing, newExternalOIDCArchitectureEnabled)...) - validationErrs = append(validationErrs, validateOAuthRoutes(ctx, tc.routeClient, tc.configClient, requireMissing)...) - validationErrs = append(validationErrs, validateOAuthControllerConditions(tc.operatorClient, requireMissing, newExternalOIDCArchitectureEnabled)...) - validationErrs = append(validationErrs, validateOperandVersions(ctx, tc.configClient, requireMissing, newExternalOIDCArchitectureEnabled)...) - validationErrs = append(validationErrs, validateOAuthRelatedObjects(ctx, tc.configClient, requireMissing)...) - return len(validationErrs) == 0, nil - }) - - require.NoError(t, utilerrors.NewAggregate(validationErrs), "failed to validate OAuth state") - require.NoError(t, waitErr, "failed to wait for OAuth state validation") -} - -func validateOAuthResources(ctx context.Context, dynamicClient *dynamic.DynamicClient, requireMissing, newExternalOIDCArchitectureEnabled bool) []error { - errs := make([]error, 0) - - type resourceReference struct { - gvr schema.GroupVersionResource - namespace string - name string - } - - resourceReferences := []resourceReference{ - {schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"}, "openshift-authentication", "v4-0-config-system-cliconfig"}, - {schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"}, "openshift-authentication", "v4-0-config-system-metadata"}, - {schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"}, "openshift-authentication", "v4-0-config-system-service-ca"}, - {schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"}, "openshift-authentication", "v4-0-config-system-trusted-ca-bundle"}, - {schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"}, "openshift-config-managed", "oauth-serving-cert"}, - {schema.GroupVersionResource{Group: "", Version: "v1", Resource: "secrets"}, "openshift-authentication", "v4-0-config-system-ocp-branding-template"}, - {schema.GroupVersionResource{Group: "", Version: "v1", Resource: "secrets"}, "openshift-authentication", "v4-0-config-system-session"}, - {schema.GroupVersionResource{Group: "", Version: "v1", Resource: "serviceaccounts"}, "openshift-authentication", "oauth-openshift"}, - {schema.GroupVersionResource{Group: "", Version: "v1", Resource: "services"}, "openshift-authentication", "oauth-openshift"}, - {schema.GroupVersionResource{Group: "apiregistration.k8s.io", Version: "v1", Resource: "apiservices"}, "", "v1.oauth.openshift.io"}, - {schema.GroupVersionResource{Group: "apiregistration.k8s.io", Version: "v1", Resource: "apiservices"}, "", "v1.user.openshift.io"}, - {schema.GroupVersionResource{Group: "oauth.openshift.io", Version: "v1", Resource: "oauthclients"}, "", "openshift-browser-client"}, - {schema.GroupVersionResource{Group: "oauth.openshift.io", Version: "v1", Resource: "oauthclients"}, "", "openshift-challenging-client"}, - {schema.GroupVersionResource{Group: "oauth.openshift.io", Version: "v1", Resource: "oauthclients"}, "", "openshift-cli-client"}, - {schema.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "clusterrolebindings"}, "", "system:openshift:openshift-authentication"}, - {schema.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "clusterrolebindings"}, "", "system:openshift:useroauthaccesstoken-manager"}, - {schema.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "clusterroles"}, "", "system:openshift:useroauthaccesstoken-manager"}, - {schema.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "rolebindings"}, "openshift-config-managed", "system:openshift:oauth-servercert-trust"}, - {schema.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "roles"}, "openshift-config-managed", "system:openshift:oauth-servercert-trust"}, - } - - // when running under the new architecture, the following resources should - // always be present and shouldn't be included in our checks - if !newExternalOIDCArchitectureEnabled { - resourceReferences = append(resourceReferences, - resourceReference{schema.GroupVersionResource{Group: "", Version: "v1", Resource: "secrets"}, "openshift-config", "webhook-authentication-integrated-oauth"}, - resourceReference{schema.GroupVersionResource{Group: "", Version: "v1", Resource: "services"}, "openshift-oauth-apiserver", "api"}, - resourceReference{schema.GroupVersionResource{Group: "", Version: "v1", Resource: "serviceaccounts"}, "openshift-oauth-apiserver", "oauth-apiserver-sa"}, - resourceReference{schema.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "clusterrolebindings"}, "", "system:openshift:oauth-apiserver"}, - ) - } - - for _, obj := range resourceReferences { - _, err := dynamicClient.Resource(obj.gvr).Namespace(obj.namespace).Get(ctx, obj.name, metav1.GetOptions{}) - if err != nil && !errors.IsNotFound(err) { - errs = append(errs, fmt.Errorf("unexpected error while getting resource %s/%s: %v", obj.namespace, obj.name, err)) - } else if requireMissing != errors.IsNotFound(err) { - errs = append(errs, fmt.Errorf("resource %s '%s/%s' wanted missing: %v; got: %v (error: %v)", obj.gvr.String(), obj.namespace, obj.name, requireMissing, errors.IsNotFound(err), err)) - } - } - - return errs -} - -func validateOAuthRoutes(ctx context.Context, routeClient routeclient.Interface, configClient *configclient.Clientset, requireMissing bool) []error { - errs := make([]error, 0) - for _, obj := range []struct{ namespace, name string }{ - {"openshift-authentication", "oauth-openshift"}, - } { - _, err := routeClient.RouteV1().Routes(obj.namespace).Get(ctx, obj.name, metav1.GetOptions{}) - if err != nil && !errors.IsNotFound(err) { - errs = append(errs, fmt.Errorf("unexpected error while getting route %s/%s: %v", obj.namespace, obj.name, err)) - } else if requireMissing != errors.IsNotFound(err) { - errs = append(errs, fmt.Errorf("route %s/%s wanted missing: %v; got: %v", obj.namespace, obj.name, requireMissing, !errors.IsNotFound((err)))) - } - - // ingress status - ingress, err := configClient.ConfigV1().Ingresses().Get(ctx, "cluster", metav1.GetOptions{}) - if err != nil { - return append(errs, err) - } - - found := false - for _, route := range ingress.Status.ComponentRoutes { - if route.Name == obj.name && route.Namespace == obj.namespace { - found = true - break - } - } - - if !requireMissing && !found { - errs = append(errs, fmt.Errorf("route %s required but was not found", obj)) - } else if requireMissing && found { - errs = append(errs, fmt.Errorf("route %s required to be missing but was found", obj)) - } - } - - return errs -} - -func validateOAuthControllerConditions(operatorClient v1helpers.OperatorClient, requireMissing, newExternalOIDCArchitectureEnabled bool) []error { - errs := make([]error, 0) - controllerConditionTypes := sets.New[string]( - // endpointAccessibleController - "OAuthServerRouteEndpointAccessibleControllerAvailable", - "OAuthServerServiceEndpointAccessibleControllerAvailable", - "OAuthServerServiceEndpointsEndpointAccessibleControllerAvailable", - // payloadConfigController - "OAuthConfigDegraded", - "OAuthSessionSecretDegraded", - "OAuthConfigRouteDegraded", - "OAuthConfigIngressDegraded", - "OAuthConfigServiceDegraded", - // ingressNodesAvailableController - "ReadyIngressNodesAvailable", - // ingressStateController - "IngressStateEndpointsDegraded", - "IngressStatePodsDegraded", - // metadataController - "IngressConfigDegraded", - "AuthConfigDegraded", - "OAuthSystemMetadataDegraded", - // routerCertsDomainValidationController - "RouterCertsDegraded", - // serviceCAController - "OAuthServiceDegraded", - "SystemServiceCAConfigDegraded", - // wellKnownReadyController - "WellKnownAvailable", - "WellKnownReadyControllerDegradationObserved", - ) - - if !newExternalOIDCArchitectureEnabled { - // webhookAuthenticatorController - controllerConditionTypes.Insert("AuthenticatorCertKeyProgressing") - } - - _, operatorStatus, _, err := operatorClient.GetOperatorState() - if err != nil { - return append(errs, err) - } - - allConditions := sets.New[string]() - for _, condition := range operatorStatus.Conditions { - allConditions.Insert(condition.Type) - } - - if requireMissing { - // no controller conditions must exist in operator status - if intersection := controllerConditionTypes.Intersection(allConditions); intersection.Len() > 0 { - return append(errs, fmt.Errorf("expected conditions to be missing but were found: %v", intersection.UnsortedList())) - } - return nil - } - - if diff := controllerConditionTypes.Difference(allConditions); diff.Len() > 0 { - // all controller conditions must exist in operator status - return append(errs, fmt.Errorf("expected conditions to exist, but were not found: %v", diff.UnsortedList())) - } - - return nil -} - -func validateOperandVersions(ctx context.Context, cfgClient *configclient.Clientset, requireMissing, newExternalOIDCArchitectureEnabled bool) []error { - operands := sets.New("oauth-openshift") - - // If the new architecture for external OIDC is not enabled, - // test this as we always did and ensure that the oauth-apiserver - // operand version gets removed appropriately. - if !newExternalOIDCArchitectureEnabled { - operands.Insert("oauth-apiserver") - } - - authnClusterOperator, err := cfgClient.ConfigV1().ClusterOperators().Get(ctx, "authentication", metav1.GetOptions{}) - if err != nil { - return []error{fmt.Errorf("fetching authentication ClusterOperator: %w", err)} - } - - foundOperands := []string{} - for _, version := range authnClusterOperator.Status.Versions { - if operands.Has(version.Name) { - foundOperands = append(foundOperands, version.Name) - } - } - - if requireMissing && len(foundOperands) > 0 { - return []error{fmt.Errorf("authentication ClusterOperator status has operands %v in versions when they should be unset", foundOperands)} - } - - foundSet := sets.New(foundOperands...) - if !requireMissing && !foundSet.Equal(operands) { - return []error{fmt.Errorf("authentication ClusterOperator status expected to have operands %v in versions but got %v", operands.UnsortedList(), foundOperands)} - } - - // If the new architecture for external OIDC is enabled, - // ensure the oauth-apiserver operand is always present. - if newExternalOIDCArchitectureEnabled { - found := false - for _, version := range authnClusterOperator.Status.Versions { - if version.Name == "oauth-apiserver" { - found = true - break - } - } - - if !found { - return []error{fmt.Errorf("authentication ClusterOperator status expected to have operand \"oauth-apiserver\" in versions but it was missing")} - } - } - - return nil -} - -func validateOAuthRelatedObjects(ctx context.Context, configClient *configclient.Clientset, requireMissing bool) []error { - co, err := configClient.ConfigV1().ClusterOperators().Get(ctx, "authentication", metav1.GetOptions{}) - if err != nil { - return []error{err} - } - - oauthRelatedObjects := []configv1.ObjectReference{ - {Group: routev1.GroupName, Resource: "routes", Name: "oauth-openshift", Namespace: "openshift-authentication"}, - {Resource: "services", Name: "oauth-openshift", Namespace: "openshift-authentication"}, - } - - errs := make([]error, 0) - for _, oauthObj := range oauthRelatedObjects { - found := false - for _, existingObj := range co.Status.RelatedObjects { - if oauthObj.Group == existingObj.Group && - oauthObj.Resource == existingObj.Resource && - oauthObj.Name == existingObj.Name && - oauthObj.Namespace == existingObj.Namespace { - found = true - break - } - } - - if requireMissing && found { - errs = append(errs, fmt.Errorf("oauth related object %s/%s %s/%s should be missing but was found in RelatedObjects", - oauthObj.Group, oauthObj.Resource, oauthObj.Namespace, oauthObj.Name)) - } else if !requireMissing && !found { - errs = append(errs, fmt.Errorf("oauth related object %s/%s %s/%s should be present but was not found in RelatedObjects", - oauthObj.Group, oauthObj.Resource, oauthObj.Namespace, oauthObj.Name)) - } - } - - return errs -} - -func (tc *testClient) testOIDCAuthentication(t *testing.T, ctx context.Context, kcClient *test.KeycloakClient, usernameClaim, usernamePrefix string, expectAuthSuccess bool) { - // re-authenticate to ensure we always have a fresh token - var err error - waitErr := wait.PollUntilContextTimeout(ctx, 5*time.Second, 30*time.Second, true, func(ctx context.Context) (bool, error) { - err = kcClient.AuthenticatePassword(oidcClientId, "", "admin", "password") - return err == nil, nil - }) - require.NoError(t, err, "failed to authenticate to keycloak: %v", err) - require.NoError(t, waitErr, "failed to wait for keycloak authentication: %v", waitErr) - - group := names.SimpleNameGenerator.GenerateName("e2e-keycloak-group-") - err = kcClient.CreateGroup(group) - require.NoError(t, err) - - user := names.SimpleNameGenerator.GenerateName("e2e-keycloak-user-") - email := fmt.Sprintf("%s@test.dev", user) - password := "password" - firstName := "Homer" - lastName := "Simpson" - err = kcClient.CreateUser( - user, - email, - password, - []string{group}, - map[string]string{ - "firstName": firstName, - "lastName": lastName, - }, - ) - require.NoError(t, err) - - // use a keycloak client for the user created above to fetch its tokens - transport, err := rest.TransportFor(tc.kubeConfig) - require.NoError(t, err) - userClient := test.KeycloakClientFor(t, transport, kcClient.IssuerURL(), "master") - err = userClient.AuthenticatePassword(oidcClientId, "", user, password) - require.NoError(t, err) - accessTokenStr, idTokenStr := userClient.Tokens() - require.NotEmpty(t, accessTokenStr, "access token must not be empty") - require.NotEmpty(t, idTokenStr, "id token must not be empty") - - // fetch issuer's JWKS and use it to parse JWT tokens - issuerJWKS, err := fetchIssuerJWKS(kcClient.IssuerURL()) - require.NoError(t, err) - require.NotNil(t, issuerJWKS) - keyfunc := extractRSAPubKeyFunc(issuerJWKS) - - accessToken, err := jwt.ParseWithClaims(accessTokenStr, &expectedClaims{}, keyfunc) - require.NoError(t, err) - require.NotNil(t, accessToken) - - idToken, err := jwt.ParseWithClaims(idTokenStr, &expectedClaims{}, keyfunc) - require.NoError(t, err) - require.NotNil(t, idToken) - - // validate the contents of the OIDC tokens - actualAccessTokenClaims := accessToken.Claims.(*expectedClaims) - require.True(t, accessToken.Valid) - require.Equal(t, userClient.IssuerURL(), actualAccessTokenClaims.Issuer) - require.Equal(t, user, actualAccessTokenClaims.PreferredUsername) - require.Equal(t, email, actualAccessTokenClaims.Email) - require.Equal(t, "Bearer", actualAccessTokenClaims.Type) - require.Equal(t, firstName, actualAccessTokenClaims.GivenName) - require.Equal(t, lastName, actualAccessTokenClaims.FamilyName) - require.Equal(t, fmt.Sprintf("%s %s", firstName, lastName), actualAccessTokenClaims.Name) - require.NotEmpty(t, actualAccessTokenClaims.Subject) - - actualIDTokenClaims := idToken.Claims.(*expectedClaims) - require.True(t, idToken.Valid) - require.Equal(t, userClient.IssuerURL(), actualIDTokenClaims.Issuer) - require.Equal(t, user, actualIDTokenClaims.PreferredUsername) - require.Equal(t, email, actualIDTokenClaims.Email) - require.Equal(t, "ID", actualIDTokenClaims.Type) - require.Equal(t, jwt.ClaimStrings{oidcClientId}, actualIDTokenClaims.Audience) - require.Equal(t, firstName, actualIDTokenClaims.GivenName) - require.Equal(t, lastName, actualIDTokenClaims.FamilyName) - require.Equal(t, fmt.Sprintf("%s %s", firstName, lastName), actualIDTokenClaims.Name) - require.NotEmpty(t, actualIDTokenClaims.Subject) - - // test authentication via the kube-apiserver - // create a new kube client that uses the OIDC id_token as a bearer token - kubeConfig := rest.AnonymousClientConfig(tc.kubeConfig) - kubeConfig.BearerToken = idTokenStr - kubeClient, err := kubernetes.NewForConfig(kubeConfig) - require.NoError(t, err) - - ssr, err := kubeClient.AuthenticationV1().SelfSubjectReviews().Create(ctx, &authenticationv1.SelfSubjectReview{}, metav1.CreateOptions{}) - if expectAuthSuccess { - // test authentication with the OIDC token using a self subject review - expectedUsername := "" - switch usernameClaim { - case "email": - expectedUsername = usernamePrefix + email - case "sub": - expectedUsername = usernamePrefix + actualIDTokenClaims.Subject - default: - t.Fatalf("unexpected username claim: %s", usernameClaim) - } - - require.NoError(t, err) - require.NotNil(t, ssr) - require.Contains(t, ssr.Status.UserInfo.Groups, "system:authenticated") - require.Equal(t, expectedUsername, ssr.Status.UserInfo.Username) - } else { - require.Error(t, err) - require.True(t, errors.IsUnauthorized(err)) - } -} - -func (tc *testClient) requireKASRolloutSuccessful(t *testing.T, testCtx context.Context, authSpec *configv1.AuthenticationSpec, kasOriginalRevision int32, expectedUsernamePrefix string) { - // wait for KAS rollout - err := test.WaitForNewKASRollout(t, testCtx, tc.operatorConfigClient.OperatorV1().KubeAPIServers(), kasOriginalRevision) - require.NoError(t, err, "failed to wait for KAS rollout") - - kasRevision := tc.validateKASConfig(t, testCtx) - tc.validateAuthConfigJSON(t, testCtx, authSpec, expectedUsernamePrefix, oidcGroupsClaim, oidcGroupsPrefix, kasRevision) -} - -func (tc *testClient) authResourceRollback(ctx context.Context, origAuthSpec *configv1.AuthenticationSpec) error { - auth, err := tc.configClient.ConfigV1().Authentications().Get(ctx, "cluster", metav1.GetOptions{}) - if err != nil { - return fmt.Errorf("rollback failed for authentication '%s' while retrieving fresh object: %v", auth.Name, err) - } - - if !equality.Semantic.DeepEqual(auth.Spec, *origAuthSpec) { - auth.Spec = *origAuthSpec - if _, err := tc.configClient.ConfigV1().Authentications().Update(ctx, auth, metav1.UpdateOptions{}); err != nil { - return fmt.Errorf("rollback failed for authentication '%s' while updating object: %v", auth.Name, err) - } - } - - return nil -} - -func featureGateEnabled(ctx context.Context, configClient *configclient.Clientset, feature configv1.FeatureGateName) bool { - featureGates, err := configClient.ConfigV1().FeatureGates().Get(ctx, "cluster", metav1.GetOptions{}) - if err != nil { - return false - } - - if len(featureGates.Status.FeatureGates) == 0 { - return false - } - - for _, enabled := range featureGates.Status.FeatureGates[0].Enabled { - if enabled.Name == feature { - return true - } - } - - return false +// This test calls the shared test function which +// can be called from both standard Go tests and Ginkgo tests. +// +// This situation is temporary until we verify the new e2e-openshift-cluster-authentication-oidc-ote job. +// Eventually all tests will be run only as part of the OTE framework. +func TestExternalOIDCWithKeycloak(tt *testing.T) { + testExternalOIDCWithKeycloak(tt.Context(), tt) } diff --git a/test/e2e/certs.go b/test/e2e/certs.go index b5b1e0dcad..1108929c8e 100644 --- a/test/e2e/certs.go +++ b/test/e2e/certs.go @@ -24,7 +24,7 @@ import ( ) var _ = g.Describe("[sig-auth] authentication operator", func() { - g.It("[Operator][Certs] TestRouterCerts", func() { + g.It("[Parallel][Certs] TestRouterCerts", func() { testRouterCerts(g.GinkgoTB()) }) }) diff --git a/test/e2e/custom_route.go b/test/e2e/custom_route.go index 68104bcfea..56aa97674a 100644 --- a/test/e2e/custom_route.go +++ b/test/e2e/custom_route.go @@ -29,7 +29,7 @@ import ( ) var _ = g.Describe("[sig-auth] authentication operator", func() { - g.It("[Operator][Routes] TestCustomRouterCerts", func() { + g.It("[Parallel][Routes] TestCustomRouterCerts", func() { testCustomRouterCerts(g.GinkgoTB()) }) }) diff --git a/test/e2e/gitlab.go b/test/e2e/gitlab.go index c23cc27ae9..8a95dbc515 100644 --- a/test/e2e/gitlab.go +++ b/test/e2e/gitlab.go @@ -10,7 +10,7 @@ import ( ) var _ = g.Describe("[sig-auth] authentication operator", func() { - g.It("[OIDC][Serial] TestGitLabAsOIDCPasswordGrantCheck", func() { + g.It("[Serial] TestGitLabAsOIDCPasswordGrantCheck", func() { testGitLabAsOIDCPasswordGrantCheck(g.GinkgoTB()) }) }) diff --git a/test/e2e/keycloak.go b/test/e2e/keycloak.go index 792254d28e..260de50f70 100644 --- a/test/e2e/keycloak.go +++ b/test/e2e/keycloak.go @@ -26,7 +26,7 @@ import ( ) var _ = g.Describe("[sig-auth] authentication operator", func() { - g.It("[OIDC][Serial] TestKeycloakAsOIDCPasswordGrantCheckAndGroupSync", func() { + g.It("[Serial] TestKeycloakAsOIDCPasswordGrantCheckAndGroupSync", func() { testKeycloakAsOIDCPasswordGrantCheckAndGroupSync(g.GinkgoTB()) }) }) @@ -38,7 +38,7 @@ func testKeycloakAsOIDCPasswordGrantCheckAndGroupSync(t testing.TB) { clients := test.NewTestClients(t) kubeConfig := test.NewClientConfigForTest(t) - _, idpName, cleanups := test.AddKeycloakIDP(t, kubeConfig, false) + kcClient, idpName, cleanups := test.AddKeycloakIDP(t, kubeConfig, false) defer test.IDPCleanupWrapper(func() { for _, c := range cleanups { c() @@ -99,14 +99,11 @@ func testKeycloakAsOIDCPasswordGrantCheckAndGroupSync(t testing.TB) { }) require.NoError(t, err, "IDP %q did not appear in OAuth config with valid OpenID configuration", idpName) - // Get a keycloak client for the external KC URL + // Use the kcClient from AddKeycloakIDP which has extended token lifetime configured + // We need transport for HTTP client used later in token endpoint testing transport, err := rest.TransportFor(kubeConfig) require.NoError(t, err) - kcClient := test.KeycloakClientFor(t, transport, configIDP.OpenID.Issuer, "master") - err = kcClient.AuthenticatePassword("admin-cli", "", "admin", "password") - require.NoError(t, err) - client, err := kcClient.GetClientByClientID(configIDP.OpenID.ClientID) require.NoError(t, err) diff --git a/test/e2e/templates.go b/test/e2e/templates.go index 2ed94a7451..9102c9d243 100644 --- a/test/e2e/templates.go +++ b/test/e2e/templates.go @@ -21,7 +21,7 @@ import ( ) var _ = g.Describe("[sig-auth] authentication operator", func() { - g.It("[Templates] TestTemplatesConfig", func() { + g.It("[Parallel][Templates] TestTemplatesConfig", func() { testTemplatesConfig(g.GinkgoTB()) }) }) diff --git a/test/e2e/tokentimeout.go b/test/e2e/tokentimeout.go index aef0310945..27daae9590 100644 --- a/test/e2e/tokentimeout.go +++ b/test/e2e/tokentimeout.go @@ -31,7 +31,7 @@ const ( ) var _ = g.Describe("[sig-auth] authentication operator", func() { - g.It("[Tokens] TestTokenInactivityTimeout [Timeout:1h]", func() { + g.It("[Parallel][Tokens] TestTokenInactivityTimeout [Timeout:1h]", func() { testTokenInactivityTimeout(g.GinkgoTB()) }) }) diff --git a/test/library/client.go b/test/library/client.go index 2397cb2313..94a7769f00 100644 --- a/test/library/client.go +++ b/test/library/client.go @@ -144,8 +144,13 @@ func (b *testNamespaceBuilder) WithLabels(labels map[string]string) *testNamespa return b } +// WithPSaEnforcement sets the Pod Security Admission enforcement level for the namespace. +// This sets all three PSA labels (enforce, audit, warn) to ensure consistent behavior +// and prevent audit log violations when using privileged mode. func (b *testNamespaceBuilder) WithPSaEnforcement(level psapi.Level) *testNamespaceBuilder { b.ns.Labels[psapi.EnforceLevelLabel] = string(level) + b.ns.Labels[psapi.AuditLevelLabel] = string(level) + b.ns.Labels[psapi.WarnLevelLabel] = string(level) return b } diff --git a/test/library/gitlabidp.go b/test/library/gitlabidp.go index 91ba7b5e31..9a276c179c 100644 --- a/test/library/gitlabidp.go +++ b/test/library/gitlabidp.go @@ -37,9 +37,18 @@ func AddGitlabIDP( // TODO: possibly make this be a wrapper to a function to sim configclients, err := configv1client.NewForConfig(kubeconfig) require.NoError(t, err) - nsName, gitlabHost, cleanup := deployPod(t, kubeClients, routeClient, + // Deploy GitLab with ImageStream import for known-image-checker compliance + // GitLab requires privileged mode to manage system services (PostgreSQL, Redis, nginx, Sidekiq) + nsName, gitlabHost, cleanup := deployPodWithImageStream( + t, + kubeconfig, + kubeClients, + routeClient, "gitlab", - "docker.io/gitlab/gitlab-ce:13.8.4-ce.0", + "docker.io", // registry + "gitlab/gitlab-ce", // image name + "13.8.4-ce.0", // version + "gitlab", // imagestream name []corev1.EnvVar{ // configure password for GitLab root user {Name: "GITLAB_OMNIBUS_CONFIG", Value: "gitlab_rails['initial_root_password']='password';"}, @@ -85,6 +94,7 @@ func AddGitlabIDP( // TODO: possibly make this be a wrapper to a function to sim nil, nil, false, + true, // usePrivilegedSecurity = true (GitLab needs privileged for system services) ) cleanups = []func(){cleanup} diff --git a/test/library/idpdeployment.go b/test/library/idpdeployment.go index 7c6e082b4d..bbd2d9870c 100644 --- a/test/library/idpdeployment.go +++ b/test/library/idpdeployment.go @@ -11,20 +11,21 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/cache" + watchtools "k8s.io/client-go/tools/watch" "k8s.io/utils/ptr" configv1 "github.com/openshift/api/config/v1" routev1 "github.com/openshift/api/route/v1" configv1client "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1" routev1client "github.com/openshift/client-go/route/clientset/versioned/typed/route/v1" - v1 "k8s.io/api/rbac/v1" - "k8s.io/client-go/tools/cache" - watchtools "k8s.io/client-go/tools/watch" ) const servingSecretName = "serving-secret" @@ -33,11 +34,42 @@ func boolptr(b bool) *bool { return &b } -func deployPod( +// createContainerSecurityContext creates a security context based on whether privileged mode is needed. +// For privileged mode (GitLab): returns privileged security context +// For restricted mode (Keycloak): returns PodSecurity restricted:latest compliant security context +func createContainerSecurityContext(usePrivileged bool) *corev1.SecurityContext { + if usePrivileged { + return &corev1.SecurityContext{ + Privileged: boolptr(true), + } + } + + // Restricted security context compliant with PodSecurity restricted:latest policy + return &corev1.SecurityContext{ + AllowPrivilegeEscalation: boolptr(false), + RunAsNonRoot: boolptr(true), + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"ALL"}, + }, + SeccompProfile: &corev1.SeccompProfile{ + Type: corev1.SeccompProfileTypeRuntimeDefault, + }, + } +} + +// deployPodWithImageStream wraps deployPod to import external images via ImageStream first. +// This ensures pods pull from the internal registry, which is allowed by the known-image-checker monitor test. +// The function creates a namespace, imports the image FIRST, then deploys the pod using the internal registry reference. +func deployPodWithImageStream( t testing.TB, + kubeconfig *rest.Config, clients *kubernetes.Clientset, routeClient routev1client.RouteV1Interface, - name, image string, + name string, + registry string, + imageName string, + imageVersion string, + imageStreamName string, env []corev1.EnvVar, httpPort, httpsPort int32, volumes []corev1.Volume, @@ -46,31 +78,95 @@ func deployPod( readinessProbe *corev1.Probe, livenessProbe *corev1.Probe, useTLS bool, + usePrivilegedSecurity bool, command ...string, ) (namespace, host string, cleanup func()) { - testContext := context.TODO() + // Step 1: Create the namespace FIRST (before importing image or deploying pod) + nsBuilder := NewTestNamespaceBuilder("e2e-test-authentication-operator-"). + WithLabels(CAOE2ETestLabels()) + + if usePrivilegedSecurity { + nsBuilder = nsBuilder.WithPrivilegedPSaEnforcement() + } else { + nsBuilder = nsBuilder.WithRestrictedPSaEnforcement() + } - var err error - cleanup = func() {} + namespace = nsBuilder.Create(t, clients.CoreV1().Namespaces()) + + nsCleanup := func() { + // remove the NS, it will take away all the resources created here along with it + if err := clients.CoreV1().Namespaces().Delete(context.TODO(), namespace, metav1.DeleteOptions{}); err != nil { + t.Logf("failed to remove namespace %q: %v", namespace, err) + } + } - namespace = NewTestNamespaceBuilder("e2e-test-authentication-operator-"). - WithPrivilegedPSaEnforcement(). - WithLabels(CAOE2ETestLabels()). - Create(t, clients.CoreV1().Namespaces()) + // Step 2: Import the image into an ImageStream BEFORE deploying the pod + t.Logf("Importing %s image into ImageStream for known-image-checker compliance", name) + internalImage, isCleanup, err := ImportImageToImageStream( + t, + kubeconfig, + namespace, + registry, + imageName, + imageVersion, + imageStreamName, + ) + if err != nil { + // If ImageStream import fails, clean up the namespace and fail + nsCleanup() + t.Fatalf("failed to import %s image to ImageStream: %v", name, err) + } + // Step 3: Deploy the pod using the INTERNAL image from the beginning + // This ensures the pod never pulls from external registry + host, podCleanup := deployPodInNamespace( + t, clients, routeClient, + namespace, // Use the namespace we created + name, internalImage, // Use internal image from the start! + env, httpPort, httpsPort, + volumes, volumeMounts, resources, + readinessProbe, livenessProbe, + useTLS, usePrivilegedSecurity, + command..., + ) + + t.Logf("Successfully deployed %s using internal registry image: %s", name, internalImage) + + // Return combined cleanup function cleanup = func() { - // remove the NS, it will take away all the resources create here along with it - if err := clients.CoreV1().Namespaces().Delete(testContext, namespace, metav1.DeleteOptions{}); err != nil { - t.Logf("error cleaning up a resource: %v", err) - } + isCleanup() + podCleanup() + nsCleanup() } - defer func() { - if err != nil { - cleanup() - } - }() + return namespace, host, cleanup +} + +// deployPodInNamespace deploys a pod in an existing namespace. +// This is used by deployPodWithImageStream after namespace and ImageStream creation. +func deployPodInNamespace( + t testing.TB, + clients *kubernetes.Clientset, + routeClient routev1client.RouteV1Interface, + namespace string, // Existing namespace + name, image string, + env []corev1.EnvVar, + httpPort, httpsPort int32, + volumes []corev1.Volume, + volumeMounts []corev1.VolumeMount, + resources corev1.ResourceRequirements, + readinessProbe *corev1.Probe, + livenessProbe *corev1.Probe, + useTLS bool, + usePrivilegedSecurity bool, + command ...string, +) (host string, cleanup func()) { + testContext := context.TODO() + var err error + cleanup = func() {} + + // Create service account _, err = clients.CoreV1().ServiceAccounts(namespace).Create( testContext, &corev1.ServiceAccount{ @@ -80,9 +176,10 @@ func deployPod( }, metav1.CreateOptions{}, ) + require.NoError(t, err) saName := name - pod := podTemplate(name, image, httpPort, httpsPort, command...) + pod := podTemplate(name, image, httpPort, httpsPort, usePrivilegedSecurity, command...) pod.Spec.Volumes = volumes pod.Spec.Containers[0].VolumeMounts = volumeMounts pod.Spec.Containers[0].Env = env @@ -95,6 +192,29 @@ func deployPod( } pod.Spec.ServiceAccountName = saName + // Grant privileged SCC when required (e.g., GitLab needs root access). + if usePrivilegedSecurity { + roleBinding := &rbacv1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "privileged-scc-to-default-sa", + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "ClusterRole", + Name: "system:openshift:scc:privileged", + }, + Subjects: []rbacv1.Subject{ + { + Kind: "ServiceAccount", + Name: saName, + }, + }, + } + + _, err = clients.RbacV1().RoleBindings(namespace).Create(testContext, roleBinding, metav1.CreateOptions{}) + require.NoError(t, err) + } + deployment := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: name, @@ -110,26 +230,6 @@ func deployPod( }, } - roleBinding := &v1.RoleBinding{ - ObjectMeta: metav1.ObjectMeta{ - Name: "privileged-scc-to-default-sa", - }, - RoleRef: v1.RoleRef{ - APIGroup: "rbac.authorization.k8s.io", - Kind: "ClusterRole", - Name: "system:openshift:scc:privileged", - }, - Subjects: []v1.Subject{ - { - Kind: "ServiceAccount", - Name: saName, - }, - }, - } - - _, err = clients.RbacV1().RoleBindings(namespace).Create(testContext, roleBinding, metav1.CreateOptions{}) - require.NoError(t, err) - _, err = clients.AppsV1().Deployments(namespace).Create(testContext, deployment, metav1.CreateOptions{}) require.NoError(t, err) @@ -156,10 +256,16 @@ func deployPod( host, err = WaitForRouteAdmitted(t, routeClient, route.Name, route.Namespace) require.NoError(t, err) - return + // Cleanup function only cleans up resources within the namespace, not the namespace itself + cleanup = func() { + // The namespace will be cleaned up by the caller + // We don't need to clean up individual resources here since they'll be deleted with the namespace + } + + return host, cleanup } -func podTemplate(name, image string, httpPort, httpsPort int32, command ...string) *corev1.Pod { +func podTemplate(name, image string, httpPort, httpsPort int32, usePrivilegedSecurity bool, command ...string) *corev1.Pod { return &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: name, @@ -170,11 +276,9 @@ func podTemplate(name, image string, httpPort, httpsPort int32, command ...strin Spec: corev1.PodSpec{ Containers: []corev1.Container{ { - Name: "payload", - Image: image, - SecurityContext: &corev1.SecurityContext{ - Privileged: boolptr(true), - }, + Name: "payload", + Image: image, + SecurityContext: createContainerSecurityContext(usePrivilegedSecurity), Ports: []corev1.ContainerPort{ { ContainerPort: httpsPort, diff --git a/test/library/imagestream.go b/test/library/imagestream.go new file mode 100644 index 0000000000..911a46f74a --- /dev/null +++ b/test/library/imagestream.go @@ -0,0 +1,129 @@ +package library + +import ( + "context" + "fmt" + "strings" + "testing" + "time" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/rest" + + imagev1 "github.com/openshift/api/image/v1" + imagev1client "github.com/openshift/client-go/image/clientset/versioned/typed/image/v1" +) + +// ImportImageToImageStream imports an external image into a namespace's ImageStream. +// This ensures the image is pulled from the cluster's internal registry, which is allowed +// by the known-image-checker monitor test. +// +// Parameters: +// - registry: image registry (e.g., "quay.io", "docker.io") +// - name: image name (e.g., "keycloak/keycloak", "gitlab/gitlab-ce") +// - version: image tag/version (e.g., "25.0", "13.8.4-ce.0") +// - imageStreamName: the name for the ImageStream (e.g., "keycloak", "gitlab") +// +// Returns: +// - internalImage: the internal registry reference that pods should use +// - cleanup: function to delete the ImageStream +// - error: any error that occurred +func ImportImageToImageStream( + t testing.TB, + kubeconfig *rest.Config, + namespace string, + registry string, + name string, + version string, + imageStreamName string, +) (string, func(), error) { + ctx := context.Background() + + imageClient, err := imagev1client.NewForConfig(kubeconfig) + if err != nil { + return "", nil, fmt.Errorf("failed to create image client: %w", err) + } + + // Construct the full source image reference: registry/name:version + sourceImage := fmt.Sprintf("%s/%s:%s", registry, name, version) + + // Normalize registry (docker.io uses library/ prefix for official images if no org specified) + if registry == "docker.io" && !strings.Contains(name, "/") { + sourceImage = fmt.Sprintf("%s/library/%s:%s", registry, name, version) + } + + t.Logf("Importing image %s into ImageStream %s/%s with tag %s", sourceImage, namespace, imageStreamName, version) + + // Create ImageStream using the typed API structure + is := &imagev1.ImageStream{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "image.openshift.io/v1", + Kind: "ImageStream", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: imageStreamName, + Namespace: namespace, + }, + Spec: imagev1.ImageStreamSpec{ + Tags: []imagev1.TagReference{ + { + Name: version, + From: &corev1.ObjectReference{ + Kind: "DockerImage", + Name: sourceImage, + }, + ImportPolicy: imagev1.TagImportPolicy{ + Scheduled: false, // Don't auto-reimport + }, + ReferencePolicy: imagev1.TagReferencePolicy{ + Type: imagev1.LocalTagReferencePolicy, // Use local reference for pulls + }, + }, + }, + }, + } + + // Create the ImageStream using typed client + _, err = imageClient.ImageStreams(namespace).Create( + ctx, is, metav1.CreateOptions{}) + if err != nil { + return "", nil, fmt.Errorf("failed to create imagestream: %w", err) + } + + cleanup := func() { + if err := imageClient.ImageStreams(namespace).Delete( + context.Background(), imageStreamName, metav1.DeleteOptions{}); err != nil { + t.Logf("failed to delete imagestream %s/%s: %v", namespace, imageStreamName, err) + } + } + + // Wait for the image to be imported + t.Logf("Waiting for image to be imported into ImageStream %s/%s...", namespace, imageStreamName) + var internalImage string + err = wait.PollUntilContextTimeout(ctx, 5*time.Second, 3*time.Minute, true, func(pollCtx context.Context) (bool, error) { + is, err := imageClient.ImageStreams(namespace).Get( + pollCtx, imageStreamName, metav1.GetOptions{}) + if err != nil { + return false, err + } + + // Check if the tag has been imported (has a dockerImageReference) + for _, tag := range is.Status.Tags { + if tag.Tag == version && len(tag.Items) > 0 && tag.Items[0].DockerImageReference != "" { + internalImage = tag.Items[0].DockerImageReference + t.Logf("Image successfully imported: %s", internalImage) + return true, nil + } + } + return false, nil + }) + + if err != nil { + return "", cleanup, fmt.Errorf("timeout waiting for image import: %w", err) + } + + t.Logf("Image available at internal registry: %s", internalImage) + return internalImage, cleanup, nil +} diff --git a/test/library/keycloakidp.go b/test/library/keycloakidp.go index 8d3b254879..2581a81e17 100644 --- a/test/library/keycloakidp.go +++ b/test/library/keycloakidp.go @@ -62,9 +62,18 @@ func AddKeycloakIDP( InitialDelaySeconds: 10, } - nsName, keycloakHost, cleanup := deployPod(t, kubeClients, routeClient, + // Deploy Keycloak with ImageStream import for known-image-checker compliance + // Keycloak uses restricted security context (runs as non-root) + nsName, keycloakHost, cleanup := deployPodWithImageStream( + t, + kubeconfig, + kubeClients, + routeClient, "keycloak", - "quay.io/keycloak/keycloak:25.0", + "quay.io", // registry + "keycloak/keycloak", // image name + "25.0", // version + "keycloak", // imagestream name []corev1.EnvVar{ // configure password for Keycloak root user {Name: "KEYCLOAK_ADMIN", Value: "admin"}, @@ -103,6 +112,7 @@ func AddKeycloakIDP( &readinessProbe, &livenessProbe, true, + false, // usePrivilegedSecurity = false (Keycloak runs with restricted PSA) "/opt/keycloak/bin/kc.sh", "start-dev", ) cleanups = []func(){cleanup} @@ -249,6 +259,7 @@ func KeycloakClientFor(t testing.TB, transport http.RoundTripper, keycloakURL, k client := &http.Client{ Transport: transport, + Timeout: 10 * time.Second, } c := &KeycloakClient{ @@ -273,7 +284,9 @@ func (kc *KeycloakClient) AuthenticatePassword(clientID, clientSecret, name, pas data.Add("client_secret", clientSecret) } - authReq, err := http.NewRequest(http.MethodPost, kc.TokenURL(), bytes.NewBufferString(data.Encode())) + reqCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + authReq, err := http.NewRequestWithContext(reqCtx, http.MethodPost, kc.TokenURL(), bytes.NewBufferString(data.Encode())) if err != nil { return err } @@ -314,7 +327,7 @@ func (kc *KeycloakClient) AuthenticatePassword(clientID, clientSecret, name, pas idToken, err := retrieveValue("id_token", authResp) if err != nil { - return nil + return err } kc.idToken = idToken diff --git a/test/library/waits.go b/test/library/waits.go index 692896b411..0c491a135c 100644 --- a/test/library/waits.go +++ b/test/library/waits.go @@ -78,7 +78,7 @@ func WaitForClusterOperatorStatus(t testing.TB, client configv1client.ConfigV1In // WaitForClusterOperatorStatusStable checks that the specified cluster operator's status does not diverge // from the conditions specified for 10 minutes. It returns nil if all conditions were matching expectations for that // period, and an error otherwise. -func WaitForClusterOperatorStatusStable(t *testing.T, ctx context.Context, client configv1client.ConfigV1Interface, name string, requiredConditions ...configv1.ClusterOperatorStatusCondition) error { +func WaitForClusterOperatorStatusStable(t testing.TB, ctx context.Context, client configv1client.ConfigV1Interface, name string, requiredConditions ...configv1.ClusterOperatorStatusCondition) error { t.Logf("will wait up to 10m for clusteroperators.config.openshift.io/%s status to be stable: %v", name, conditionsStatusString(requiredConditions)) var endConditions []configv1.ClusterOperatorStatusCondition @@ -157,7 +157,7 @@ func WaitForHTTPStatus(t testing.TB, waitDuration time.Duration, client *http.Cl }) } -func WaitForNewKASRollout(t *testing.T, ctx context.Context, kasClient operatorv1client.KubeAPIServerInterface, origRevision int32) error { +func WaitForNewKASRollout(t testing.TB, ctx context.Context, kasClient operatorv1client.KubeAPIServerInterface, origRevision int32) error { t.Logf("will wait for KAS rollout; latest available revision: %d", origRevision) var latestRevision int32 err := wait.PollUntilContextTimeout(ctx, 10*time.Second, 30*time.Minute, true, func(ctx context.Context) (bool, error) { @@ -189,7 +189,7 @@ func WaitForNewKASRollout(t *testing.T, ctx context.Context, kasClient operatorv return nil } -func WaitForClusterOperatorStatusAlwaysAvailable(t *testing.T, ctx context.Context, client configv1client.ConfigV1Interface, name string) error { +func WaitForClusterOperatorStatusAlwaysAvailable(t testing.TB, ctx context.Context, client configv1client.ConfigV1Interface, name string) error { return WaitForClusterOperatorStatusStable(t, ctx, client, name, configv1.ClusterOperatorStatusCondition{Type: configv1.OperatorAvailable, Status: configv1.ConditionTrue}, configv1.ClusterOperatorStatusCondition{Type: configv1.OperatorDegraded, Status: configv1.ConditionFalse}, diff --git a/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/image.go b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/image.go new file mode 100644 index 0000000000..5dea79f635 --- /dev/null +++ b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/image.go @@ -0,0 +1,378 @@ +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1 + +import ( + imagev1 "github.com/openshift/api/image/v1" + internal "github.com/openshift/client-go/image/applyconfigurations/internal" + apismetav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + types "k8s.io/apimachinery/pkg/types" + managedfields "k8s.io/apimachinery/pkg/util/managedfields" + metav1 "k8s.io/client-go/applyconfigurations/meta/v1" +) + +// ImageApplyConfiguration represents a declarative configuration of the Image type for use +// with apply. +// +// Image is an immutable representation of a container image and its metadata at a point in time. +// Images are named by taking a hash of their contents (metadata and content) and any change +// in format, content, or metadata results in a new name. The images resource is primarily +// for use by cluster administrators and integrations like the cluster image registry - end +// users, instead, access images via the imagestreamtags or imagestreamimages resources. While +// image metadata is stored in the API, any integration that implements the container image +// registry API must provide its own storage for the raw manifest data, image config, and +// layer contents. +// +// Compatibility level 1: Stable within a major release for a minimum of 12 months or 3 minor releases (whichever is longer). +type ImageApplyConfiguration struct { + metav1.TypeMetaApplyConfiguration `json:",inline"` + // metadata is the standard object's metadata. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata + *metav1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"` + // dockerImageReference is the string that can be used to pull this image. + DockerImageReference *string `json:"dockerImageReference,omitempty"` + // dockerImageMetadata contains metadata about this image + DockerImageMetadata *runtime.RawExtension `json:"dockerImageMetadata,omitempty"` + // dockerImageMetadataVersion conveys the version of the object, which if empty defaults to "1.0" + DockerImageMetadataVersion *string `json:"dockerImageMetadataVersion,omitempty"` + // dockerImageManifest is the raw JSON of the manifest + DockerImageManifest *string `json:"dockerImageManifest,omitempty"` + // dockerImageLayers represents the layers in the image. May not be set if the image does not define that data or if the image represents a manifest list. + DockerImageLayers []ImageLayerApplyConfiguration `json:"dockerImageLayers,omitempty"` + // signatures holds all signatures of the image. + Signatures []ImageSignatureApplyConfiguration `json:"signatures,omitempty"` + // dockerImageSignatures provides the signatures as opaque blobs. This is a part of manifest schema v1. + DockerImageSignatures [][]byte `json:"dockerImageSignatures,omitempty"` + // dockerImageManifestMediaType specifies the mediaType of manifest. This is a part of manifest schema v2. + DockerImageManifestMediaType *string `json:"dockerImageManifestMediaType,omitempty"` + // dockerImageConfig is a JSON blob that the runtime uses to set up the container. This is a part of manifest schema v2. + // Will not be set when the image represents a manifest list. + DockerImageConfig *string `json:"dockerImageConfig,omitempty"` + // dockerImageManifests holds information about sub-manifests when the image represents a manifest list. + // When this field is present, no DockerImageLayers should be specified. + DockerImageManifests []ImageManifestApplyConfiguration `json:"dockerImageManifests,omitempty"` +} + +// Image constructs a declarative configuration of the Image type for use with +// apply. +func Image(name string) *ImageApplyConfiguration { + b := &ImageApplyConfiguration{} + b.WithName(name) + b.WithKind("Image") + b.WithAPIVersion("image.openshift.io/v1") + return b +} + +// ExtractImageFrom extracts the applied configuration owned by fieldManager from +// image for the specified subresource. Pass an empty string for subresource to extract +// the main resource. Common subresources include "status", "scale", etc. +// image must be a unmodified Image API object that was retrieved from the Kubernetes API. +// ExtractImageFrom provides a way to perform a extract/modify-in-place/apply workflow. +// Note that an extracted apply configuration will contain fewer fields than what the fieldManager previously +// applied if another fieldManager has updated or force applied any of the previously applied fields. +func ExtractImageFrom(image *imagev1.Image, fieldManager string, subresource string) (*ImageApplyConfiguration, error) { + b := &ImageApplyConfiguration{} + err := managedfields.ExtractInto(image, internal.Parser().Type("com.github.openshift.api.image.v1.Image"), fieldManager, b, subresource) + if err != nil { + return nil, err + } + b.WithName(image.Name) + + b.WithKind("Image") + b.WithAPIVersion("image.openshift.io/v1") + return b, nil +} + +// ExtractImage extracts the applied configuration owned by fieldManager from +// image. If no managedFields are found in image for fieldManager, a +// ImageApplyConfiguration is returned with only the Name, Namespace (if applicable), +// APIVersion and Kind populated. It is possible that no managed fields were found for because other +// field managers have taken ownership of all the fields previously owned by fieldManager, or because +// the fieldManager never owned fields any fields. +// image must be a unmodified Image API object that was retrieved from the Kubernetes API. +// ExtractImage provides a way to perform a extract/modify-in-place/apply workflow. +// Note that an extracted apply configuration will contain fewer fields than what the fieldManager previously +// applied if another fieldManager has updated or force applied any of the previously applied fields. +func ExtractImage(image *imagev1.Image, fieldManager string) (*ImageApplyConfiguration, error) { + return ExtractImageFrom(image, fieldManager, "") +} + +func (b ImageApplyConfiguration) IsApplyConfiguration() {} + +// WithKind sets the Kind field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Kind field is set to the value of the last call. +func (b *ImageApplyConfiguration) WithKind(value string) *ImageApplyConfiguration { + b.TypeMetaApplyConfiguration.Kind = &value + return b +} + +// WithAPIVersion sets the APIVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the APIVersion field is set to the value of the last call. +func (b *ImageApplyConfiguration) WithAPIVersion(value string) *ImageApplyConfiguration { + b.TypeMetaApplyConfiguration.APIVersion = &value + return b +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *ImageApplyConfiguration) WithName(value string) *ImageApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Name = &value + return b +} + +// WithGenerateName sets the GenerateName field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the GenerateName field is set to the value of the last call. +func (b *ImageApplyConfiguration) WithGenerateName(value string) *ImageApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.GenerateName = &value + return b +} + +// WithNamespace sets the Namespace field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Namespace field is set to the value of the last call. +func (b *ImageApplyConfiguration) WithNamespace(value string) *ImageApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Namespace = &value + return b +} + +// WithUID sets the UID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the UID field is set to the value of the last call. +func (b *ImageApplyConfiguration) WithUID(value types.UID) *ImageApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.UID = &value + return b +} + +// WithResourceVersion sets the ResourceVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ResourceVersion field is set to the value of the last call. +func (b *ImageApplyConfiguration) WithResourceVersion(value string) *ImageApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.ResourceVersion = &value + return b +} + +// WithGeneration sets the Generation field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Generation field is set to the value of the last call. +func (b *ImageApplyConfiguration) WithGeneration(value int64) *ImageApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Generation = &value + return b +} + +// WithCreationTimestamp sets the CreationTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CreationTimestamp field is set to the value of the last call. +func (b *ImageApplyConfiguration) WithCreationTimestamp(value apismetav1.Time) *ImageApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.CreationTimestamp = &value + return b +} + +// WithDeletionTimestamp sets the DeletionTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionTimestamp field is set to the value of the last call. +func (b *ImageApplyConfiguration) WithDeletionTimestamp(value apismetav1.Time) *ImageApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionTimestamp = &value + return b +} + +// WithDeletionGracePeriodSeconds sets the DeletionGracePeriodSeconds field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionGracePeriodSeconds field is set to the value of the last call. +func (b *ImageApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *ImageApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionGracePeriodSeconds = &value + return b +} + +// WithLabels puts the entries into the Labels field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Labels field, +// overwriting an existing map entries in Labels field with the same key. +func (b *ImageApplyConfiguration) WithLabels(entries map[string]string) *ImageApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Labels == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Labels = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Labels[k] = v + } + return b +} + +// WithAnnotations puts the entries into the Annotations field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Annotations field, +// overwriting an existing map entries in Annotations field with the same key. +func (b *ImageApplyConfiguration) WithAnnotations(entries map[string]string) *ImageApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Annotations == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Annotations = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Annotations[k] = v + } + return b +} + +// WithOwnerReferences adds the given value to the OwnerReferences field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the OwnerReferences field. +func (b *ImageApplyConfiguration) WithOwnerReferences(values ...*metav1.OwnerReferenceApplyConfiguration) *ImageApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + if values[i] == nil { + panic("nil value passed to WithOwnerReferences") + } + b.ObjectMetaApplyConfiguration.OwnerReferences = append(b.ObjectMetaApplyConfiguration.OwnerReferences, *values[i]) + } + return b +} + +// WithFinalizers adds the given value to the Finalizers field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Finalizers field. +func (b *ImageApplyConfiguration) WithFinalizers(values ...string) *ImageApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + b.ObjectMetaApplyConfiguration.Finalizers = append(b.ObjectMetaApplyConfiguration.Finalizers, values[i]) + } + return b +} + +func (b *ImageApplyConfiguration) ensureObjectMetaApplyConfigurationExists() { + if b.ObjectMetaApplyConfiguration == nil { + b.ObjectMetaApplyConfiguration = &metav1.ObjectMetaApplyConfiguration{} + } +} + +// WithDockerImageReference sets the DockerImageReference field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DockerImageReference field is set to the value of the last call. +func (b *ImageApplyConfiguration) WithDockerImageReference(value string) *ImageApplyConfiguration { + b.DockerImageReference = &value + return b +} + +// WithDockerImageMetadata sets the DockerImageMetadata field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DockerImageMetadata field is set to the value of the last call. +func (b *ImageApplyConfiguration) WithDockerImageMetadata(value runtime.RawExtension) *ImageApplyConfiguration { + b.DockerImageMetadata = &value + return b +} + +// WithDockerImageMetadataVersion sets the DockerImageMetadataVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DockerImageMetadataVersion field is set to the value of the last call. +func (b *ImageApplyConfiguration) WithDockerImageMetadataVersion(value string) *ImageApplyConfiguration { + b.DockerImageMetadataVersion = &value + return b +} + +// WithDockerImageManifest sets the DockerImageManifest field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DockerImageManifest field is set to the value of the last call. +func (b *ImageApplyConfiguration) WithDockerImageManifest(value string) *ImageApplyConfiguration { + b.DockerImageManifest = &value + return b +} + +// WithDockerImageLayers adds the given value to the DockerImageLayers field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the DockerImageLayers field. +func (b *ImageApplyConfiguration) WithDockerImageLayers(values ...*ImageLayerApplyConfiguration) *ImageApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithDockerImageLayers") + } + b.DockerImageLayers = append(b.DockerImageLayers, *values[i]) + } + return b +} + +// WithSignatures adds the given value to the Signatures field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Signatures field. +func (b *ImageApplyConfiguration) WithSignatures(values ...*ImageSignatureApplyConfiguration) *ImageApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithSignatures") + } + b.Signatures = append(b.Signatures, *values[i]) + } + return b +} + +// WithDockerImageSignatures adds the given value to the DockerImageSignatures field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the DockerImageSignatures field. +func (b *ImageApplyConfiguration) WithDockerImageSignatures(values ...[]byte) *ImageApplyConfiguration { + for i := range values { + b.DockerImageSignatures = append(b.DockerImageSignatures, values[i]) + } + return b +} + +// WithDockerImageManifestMediaType sets the DockerImageManifestMediaType field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DockerImageManifestMediaType field is set to the value of the last call. +func (b *ImageApplyConfiguration) WithDockerImageManifestMediaType(value string) *ImageApplyConfiguration { + b.DockerImageManifestMediaType = &value + return b +} + +// WithDockerImageConfig sets the DockerImageConfig field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DockerImageConfig field is set to the value of the last call. +func (b *ImageApplyConfiguration) WithDockerImageConfig(value string) *ImageApplyConfiguration { + b.DockerImageConfig = &value + return b +} + +// WithDockerImageManifests adds the given value to the DockerImageManifests field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the DockerImageManifests field. +func (b *ImageApplyConfiguration) WithDockerImageManifests(values ...*ImageManifestApplyConfiguration) *ImageApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithDockerImageManifests") + } + b.DockerImageManifests = append(b.DockerImageManifests, *values[i]) + } + return b +} + +// GetKind retrieves the value of the Kind field in the declarative configuration. +func (b *ImageApplyConfiguration) GetKind() *string { + return b.TypeMetaApplyConfiguration.Kind +} + +// GetAPIVersion retrieves the value of the APIVersion field in the declarative configuration. +func (b *ImageApplyConfiguration) GetAPIVersion() *string { + return b.TypeMetaApplyConfiguration.APIVersion +} + +// GetName retrieves the value of the Name field in the declarative configuration. +func (b *ImageApplyConfiguration) GetName() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Name +} + +// GetNamespace retrieves the value of the Namespace field in the declarative configuration. +func (b *ImageApplyConfiguration) GetNamespace() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Namespace +} diff --git a/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/imagelayer.go b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/imagelayer.go new file mode 100644 index 0000000000..680723f441 --- /dev/null +++ b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/imagelayer.go @@ -0,0 +1,46 @@ +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1 + +// ImageLayerApplyConfiguration represents a declarative configuration of the ImageLayer type for use +// with apply. +// +// ImageLayer represents a single layer of the image. Some images may have multiple layers. Some may have none. +type ImageLayerApplyConfiguration struct { + // name of the layer as defined by the underlying store. + Name *string `json:"name,omitempty"` + // size of the layer in bytes as defined by the underlying store. + LayerSize *int64 `json:"size,omitempty"` + // mediaType of the referenced object. + MediaType *string `json:"mediaType,omitempty"` +} + +// ImageLayerApplyConfiguration constructs a declarative configuration of the ImageLayer type for use with +// apply. +func ImageLayer() *ImageLayerApplyConfiguration { + return &ImageLayerApplyConfiguration{} +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *ImageLayerApplyConfiguration) WithName(value string) *ImageLayerApplyConfiguration { + b.Name = &value + return b +} + +// WithLayerSize sets the LayerSize field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the LayerSize field is set to the value of the last call. +func (b *ImageLayerApplyConfiguration) WithLayerSize(value int64) *ImageLayerApplyConfiguration { + b.LayerSize = &value + return b +} + +// WithMediaType sets the MediaType field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the MediaType field is set to the value of the last call. +func (b *ImageLayerApplyConfiguration) WithMediaType(value string) *ImageLayerApplyConfiguration { + b.MediaType = &value + return b +} diff --git a/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/imagelookuppolicy.go b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/imagelookuppolicy.go new file mode 100644 index 0000000000..1a5e456562 --- /dev/null +++ b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/imagelookuppolicy.go @@ -0,0 +1,31 @@ +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1 + +// ImageLookupPolicyApplyConfiguration represents a declarative configuration of the ImageLookupPolicy type for use +// with apply. +// +// ImageLookupPolicy describes how an image stream can be used to override the image references +// used by pods, builds, and other resources in a namespace. +type ImageLookupPolicyApplyConfiguration struct { + // local will change the docker short image references (like "mysql" or + // "php:latest") on objects in this namespace to the image ID whenever they match + // this image stream, instead of reaching out to a remote registry. The name will + // be fully qualified to an image ID if found. The tag's referencePolicy is taken + // into account on the replaced value. Only works within the current namespace. + Local *bool `json:"local,omitempty"` +} + +// ImageLookupPolicyApplyConfiguration constructs a declarative configuration of the ImageLookupPolicy type for use with +// apply. +func ImageLookupPolicy() *ImageLookupPolicyApplyConfiguration { + return &ImageLookupPolicyApplyConfiguration{} +} + +// WithLocal sets the Local field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Local field is set to the value of the last call. +func (b *ImageLookupPolicyApplyConfiguration) WithLocal(value bool) *ImageLookupPolicyApplyConfiguration { + b.Local = &value + return b +} diff --git a/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/imagemanifest.go b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/imagemanifest.go new file mode 100644 index 0000000000..f1368387b5 --- /dev/null +++ b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/imagemanifest.go @@ -0,0 +1,79 @@ +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1 + +// ImageManifestApplyConfiguration represents a declarative configuration of the ImageManifest type for use +// with apply. +// +// ImageManifest represents sub-manifests of a manifest list. The Digest field points to a regular +// Image object. +type ImageManifestApplyConfiguration struct { + // digest is the unique identifier for the manifest. It refers to an Image object. + Digest *string `json:"digest,omitempty"` + // mediaType defines the type of the manifest, possible values are application/vnd.oci.image.manifest.v1+json, + // application/vnd.docker.distribution.manifest.v2+json or application/vnd.docker.distribution.manifest.v1+json. + MediaType *string `json:"mediaType,omitempty"` + // manifestSize represents the size of the raw object contents, in bytes. + ManifestSize *int64 `json:"manifestSize,omitempty"` + // architecture specifies the supported CPU architecture, for example `amd64` or `ppc64le`. + Architecture *string `json:"architecture,omitempty"` + // os specifies the operating system, for example `linux`. + OS *string `json:"os,omitempty"` + // variant is an optional field repreenting a variant of the CPU, for example v6 to specify a particular CPU + // variant of the ARM CPU. + Variant *string `json:"variant,omitempty"` +} + +// ImageManifestApplyConfiguration constructs a declarative configuration of the ImageManifest type for use with +// apply. +func ImageManifest() *ImageManifestApplyConfiguration { + return &ImageManifestApplyConfiguration{} +} + +// WithDigest sets the Digest field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Digest field is set to the value of the last call. +func (b *ImageManifestApplyConfiguration) WithDigest(value string) *ImageManifestApplyConfiguration { + b.Digest = &value + return b +} + +// WithMediaType sets the MediaType field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the MediaType field is set to the value of the last call. +func (b *ImageManifestApplyConfiguration) WithMediaType(value string) *ImageManifestApplyConfiguration { + b.MediaType = &value + return b +} + +// WithManifestSize sets the ManifestSize field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ManifestSize field is set to the value of the last call. +func (b *ImageManifestApplyConfiguration) WithManifestSize(value int64) *ImageManifestApplyConfiguration { + b.ManifestSize = &value + return b +} + +// WithArchitecture sets the Architecture field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Architecture field is set to the value of the last call. +func (b *ImageManifestApplyConfiguration) WithArchitecture(value string) *ImageManifestApplyConfiguration { + b.Architecture = &value + return b +} + +// WithOS sets the OS field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the OS field is set to the value of the last call. +func (b *ImageManifestApplyConfiguration) WithOS(value string) *ImageManifestApplyConfiguration { + b.OS = &value + return b +} + +// WithVariant sets the Variant field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Variant field is set to the value of the last call. +func (b *ImageManifestApplyConfiguration) WithVariant(value string) *ImageManifestApplyConfiguration { + b.Variant = &value + return b +} diff --git a/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/imagesignature.go b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/imagesignature.go new file mode 100644 index 0000000000..9ba0d8fcfa --- /dev/null +++ b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/imagesignature.go @@ -0,0 +1,314 @@ +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1 + +import ( + apismetav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + metav1 "k8s.io/client-go/applyconfigurations/meta/v1" +) + +// ImageSignatureApplyConfiguration represents a declarative configuration of the ImageSignature type for use +// with apply. +// +// ImageSignature holds a signature of an image. It allows to verify image identity and possibly other claims +// as long as the signature is trusted. Based on this information it is possible to restrict runnable images +// to those matching cluster-wide policy. +// Mandatory fields should be parsed by clients doing image verification. The others are parsed from +// signature's content by the server. They serve just an informative purpose. +// +// Compatibility level 1: Stable within a major release for a minimum of 12 months or 3 minor releases (whichever is longer). +type ImageSignatureApplyConfiguration struct { + metav1.TypeMetaApplyConfiguration `json:",inline"` + // metadata is the standard object's metadata. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata + *metav1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"` + // Required: Describes a type of stored blob. + Type *string `json:"type,omitempty"` + // Required: An opaque binary string which is an image's signature. + Content []byte `json:"content,omitempty"` + // conditions represent the latest available observations of a signature's current state. + Conditions []SignatureConditionApplyConfiguration `json:"conditions,omitempty"` + // A human readable string representing image's identity. It could be a product name and version, or an + // image pull spec (e.g. "registry.access.redhat.com/rhel7/rhel:7.2"). + ImageIdentity *string `json:"imageIdentity,omitempty"` + // Contains claims from the signature. + SignedClaims map[string]string `json:"signedClaims,omitempty"` + // If specified, it is the time of signature's creation. + Created *apismetav1.Time `json:"created,omitempty"` + // If specified, it holds information about an issuer of signing certificate or key (a person or entity + // who signed the signing certificate or key). + IssuedBy *SignatureIssuerApplyConfiguration `json:"issuedBy,omitempty"` + // If specified, it holds information about a subject of signing certificate or key (a person or entity + // who signed the image). + IssuedTo *SignatureSubjectApplyConfiguration `json:"issuedTo,omitempty"` +} + +// ImageSignature constructs a declarative configuration of the ImageSignature type for use with +// apply. +func ImageSignature(name string) *ImageSignatureApplyConfiguration { + b := &ImageSignatureApplyConfiguration{} + b.WithName(name) + b.WithKind("ImageSignature") + b.WithAPIVersion("image.openshift.io/v1") + return b +} + +func (b ImageSignatureApplyConfiguration) IsApplyConfiguration() {} + +// WithKind sets the Kind field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Kind field is set to the value of the last call. +func (b *ImageSignatureApplyConfiguration) WithKind(value string) *ImageSignatureApplyConfiguration { + b.TypeMetaApplyConfiguration.Kind = &value + return b +} + +// WithAPIVersion sets the APIVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the APIVersion field is set to the value of the last call. +func (b *ImageSignatureApplyConfiguration) WithAPIVersion(value string) *ImageSignatureApplyConfiguration { + b.TypeMetaApplyConfiguration.APIVersion = &value + return b +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *ImageSignatureApplyConfiguration) WithName(value string) *ImageSignatureApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Name = &value + return b +} + +// WithGenerateName sets the GenerateName field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the GenerateName field is set to the value of the last call. +func (b *ImageSignatureApplyConfiguration) WithGenerateName(value string) *ImageSignatureApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.GenerateName = &value + return b +} + +// WithNamespace sets the Namespace field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Namespace field is set to the value of the last call. +func (b *ImageSignatureApplyConfiguration) WithNamespace(value string) *ImageSignatureApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Namespace = &value + return b +} + +// WithUID sets the UID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the UID field is set to the value of the last call. +func (b *ImageSignatureApplyConfiguration) WithUID(value types.UID) *ImageSignatureApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.UID = &value + return b +} + +// WithResourceVersion sets the ResourceVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ResourceVersion field is set to the value of the last call. +func (b *ImageSignatureApplyConfiguration) WithResourceVersion(value string) *ImageSignatureApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.ResourceVersion = &value + return b +} + +// WithGeneration sets the Generation field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Generation field is set to the value of the last call. +func (b *ImageSignatureApplyConfiguration) WithGeneration(value int64) *ImageSignatureApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Generation = &value + return b +} + +// WithCreationTimestamp sets the CreationTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CreationTimestamp field is set to the value of the last call. +func (b *ImageSignatureApplyConfiguration) WithCreationTimestamp(value apismetav1.Time) *ImageSignatureApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.CreationTimestamp = &value + return b +} + +// WithDeletionTimestamp sets the DeletionTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionTimestamp field is set to the value of the last call. +func (b *ImageSignatureApplyConfiguration) WithDeletionTimestamp(value apismetav1.Time) *ImageSignatureApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionTimestamp = &value + return b +} + +// WithDeletionGracePeriodSeconds sets the DeletionGracePeriodSeconds field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionGracePeriodSeconds field is set to the value of the last call. +func (b *ImageSignatureApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *ImageSignatureApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionGracePeriodSeconds = &value + return b +} + +// WithLabels puts the entries into the Labels field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Labels field, +// overwriting an existing map entries in Labels field with the same key. +func (b *ImageSignatureApplyConfiguration) WithLabels(entries map[string]string) *ImageSignatureApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Labels == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Labels = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Labels[k] = v + } + return b +} + +// WithAnnotations puts the entries into the Annotations field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Annotations field, +// overwriting an existing map entries in Annotations field with the same key. +func (b *ImageSignatureApplyConfiguration) WithAnnotations(entries map[string]string) *ImageSignatureApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Annotations == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Annotations = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Annotations[k] = v + } + return b +} + +// WithOwnerReferences adds the given value to the OwnerReferences field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the OwnerReferences field. +func (b *ImageSignatureApplyConfiguration) WithOwnerReferences(values ...*metav1.OwnerReferenceApplyConfiguration) *ImageSignatureApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + if values[i] == nil { + panic("nil value passed to WithOwnerReferences") + } + b.ObjectMetaApplyConfiguration.OwnerReferences = append(b.ObjectMetaApplyConfiguration.OwnerReferences, *values[i]) + } + return b +} + +// WithFinalizers adds the given value to the Finalizers field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Finalizers field. +func (b *ImageSignatureApplyConfiguration) WithFinalizers(values ...string) *ImageSignatureApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + b.ObjectMetaApplyConfiguration.Finalizers = append(b.ObjectMetaApplyConfiguration.Finalizers, values[i]) + } + return b +} + +func (b *ImageSignatureApplyConfiguration) ensureObjectMetaApplyConfigurationExists() { + if b.ObjectMetaApplyConfiguration == nil { + b.ObjectMetaApplyConfiguration = &metav1.ObjectMetaApplyConfiguration{} + } +} + +// WithType sets the Type field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Type field is set to the value of the last call. +func (b *ImageSignatureApplyConfiguration) WithType(value string) *ImageSignatureApplyConfiguration { + b.Type = &value + return b +} + +// WithContent adds the given value to the Content field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Content field. +func (b *ImageSignatureApplyConfiguration) WithContent(values ...byte) *ImageSignatureApplyConfiguration { + for i := range values { + b.Content = append(b.Content, values[i]) + } + return b +} + +// WithConditions adds the given value to the Conditions field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Conditions field. +func (b *ImageSignatureApplyConfiguration) WithConditions(values ...*SignatureConditionApplyConfiguration) *ImageSignatureApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithConditions") + } + b.Conditions = append(b.Conditions, *values[i]) + } + return b +} + +// WithImageIdentity sets the ImageIdentity field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ImageIdentity field is set to the value of the last call. +func (b *ImageSignatureApplyConfiguration) WithImageIdentity(value string) *ImageSignatureApplyConfiguration { + b.ImageIdentity = &value + return b +} + +// WithSignedClaims puts the entries into the SignedClaims field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the SignedClaims field, +// overwriting an existing map entries in SignedClaims field with the same key. +func (b *ImageSignatureApplyConfiguration) WithSignedClaims(entries map[string]string) *ImageSignatureApplyConfiguration { + if b.SignedClaims == nil && len(entries) > 0 { + b.SignedClaims = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.SignedClaims[k] = v + } + return b +} + +// WithCreated sets the Created field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Created field is set to the value of the last call. +func (b *ImageSignatureApplyConfiguration) WithCreated(value apismetav1.Time) *ImageSignatureApplyConfiguration { + b.Created = &value + return b +} + +// WithIssuedBy sets the IssuedBy field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the IssuedBy field is set to the value of the last call. +func (b *ImageSignatureApplyConfiguration) WithIssuedBy(value *SignatureIssuerApplyConfiguration) *ImageSignatureApplyConfiguration { + b.IssuedBy = value + return b +} + +// WithIssuedTo sets the IssuedTo field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the IssuedTo field is set to the value of the last call. +func (b *ImageSignatureApplyConfiguration) WithIssuedTo(value *SignatureSubjectApplyConfiguration) *ImageSignatureApplyConfiguration { + b.IssuedTo = value + return b +} + +// GetKind retrieves the value of the Kind field in the declarative configuration. +func (b *ImageSignatureApplyConfiguration) GetKind() *string { + return b.TypeMetaApplyConfiguration.Kind +} + +// GetAPIVersion retrieves the value of the APIVersion field in the declarative configuration. +func (b *ImageSignatureApplyConfiguration) GetAPIVersion() *string { + return b.TypeMetaApplyConfiguration.APIVersion +} + +// GetName retrieves the value of the Name field in the declarative configuration. +func (b *ImageSignatureApplyConfiguration) GetName() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Name +} + +// GetNamespace retrieves the value of the Namespace field in the declarative configuration. +func (b *ImageSignatureApplyConfiguration) GetNamespace() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Namespace +} diff --git a/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/imagestream.go b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/imagestream.go new file mode 100644 index 0000000000..48d3321df6 --- /dev/null +++ b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/imagestream.go @@ -0,0 +1,304 @@ +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1 + +import ( + imagev1 "github.com/openshift/api/image/v1" + internal "github.com/openshift/client-go/image/applyconfigurations/internal" + apismetav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + managedfields "k8s.io/apimachinery/pkg/util/managedfields" + metav1 "k8s.io/client-go/applyconfigurations/meta/v1" +) + +// ImageStreamApplyConfiguration represents a declarative configuration of the ImageStream type for use +// with apply. +// +// An ImageStream stores a mapping of tags to images, metadata overrides that are applied +// when images are tagged in a stream, and an optional reference to a container image +// repository on a registry. Users typically update the spec.tags field to point to external +// images which are imported from container registries using credentials in your namespace +// with the pull secret type, or to existing image stream tags and images which are +// immediately accessible for tagging or pulling. The history of images applied to a tag +// is visible in the status.tags field and any user who can view an image stream is allowed +// to tag that image into their own image streams. Access to pull images from the integrated +// registry is granted by having the "get imagestreams/layers" permission on a given image +// stream. Users may remove a tag by deleting the imagestreamtag resource, which causes both +// spec and status for that tag to be removed. Image stream history is retained until an +// administrator runs the prune operation, which removes references that are no longer in +// use. To preserve a historical image, ensure there is a tag in spec pointing to that image +// by its digest. +// +// Compatibility level 1: Stable within a major release for a minimum of 12 months or 3 minor releases (whichever is longer). +type ImageStreamApplyConfiguration struct { + metav1.TypeMetaApplyConfiguration `json:",inline"` + // metadata is the standard object's metadata. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata + *metav1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"` + // spec describes the desired state of this stream + Spec *ImageStreamSpecApplyConfiguration `json:"spec,omitempty"` + // status describes the current state of this stream + Status *ImageStreamStatusApplyConfiguration `json:"status,omitempty"` +} + +// ImageStream constructs a declarative configuration of the ImageStream type for use with +// apply. +func ImageStream(name, namespace string) *ImageStreamApplyConfiguration { + b := &ImageStreamApplyConfiguration{} + b.WithName(name) + b.WithNamespace(namespace) + b.WithKind("ImageStream") + b.WithAPIVersion("image.openshift.io/v1") + return b +} + +// ExtractImageStreamFrom extracts the applied configuration owned by fieldManager from +// imageStream for the specified subresource. Pass an empty string for subresource to extract +// the main resource. Common subresources include "status", "scale", etc. +// imageStream must be a unmodified ImageStream API object that was retrieved from the Kubernetes API. +// ExtractImageStreamFrom provides a way to perform a extract/modify-in-place/apply workflow. +// Note that an extracted apply configuration will contain fewer fields than what the fieldManager previously +// applied if another fieldManager has updated or force applied any of the previously applied fields. +func ExtractImageStreamFrom(imageStream *imagev1.ImageStream, fieldManager string, subresource string) (*ImageStreamApplyConfiguration, error) { + b := &ImageStreamApplyConfiguration{} + err := managedfields.ExtractInto(imageStream, internal.Parser().Type("com.github.openshift.api.image.v1.ImageStream"), fieldManager, b, subresource) + if err != nil { + return nil, err + } + b.WithName(imageStream.Name) + b.WithNamespace(imageStream.Namespace) + + b.WithKind("ImageStream") + b.WithAPIVersion("image.openshift.io/v1") + return b, nil +} + +// ExtractImageStream extracts the applied configuration owned by fieldManager from +// imageStream. If no managedFields are found in imageStream for fieldManager, a +// ImageStreamApplyConfiguration is returned with only the Name, Namespace (if applicable), +// APIVersion and Kind populated. It is possible that no managed fields were found for because other +// field managers have taken ownership of all the fields previously owned by fieldManager, or because +// the fieldManager never owned fields any fields. +// imageStream must be a unmodified ImageStream API object that was retrieved from the Kubernetes API. +// ExtractImageStream provides a way to perform a extract/modify-in-place/apply workflow. +// Note that an extracted apply configuration will contain fewer fields than what the fieldManager previously +// applied if another fieldManager has updated or force applied any of the previously applied fields. +func ExtractImageStream(imageStream *imagev1.ImageStream, fieldManager string) (*ImageStreamApplyConfiguration, error) { + return ExtractImageStreamFrom(imageStream, fieldManager, "") +} + +// ExtractImageStreamLayers extracts the applied configuration owned by fieldManager from +// imageStream for the layers subresource. +func ExtractImageStreamLayers(imageStream *imagev1.ImageStream, fieldManager string) (*ImageStreamApplyConfiguration, error) { + return ExtractImageStreamFrom(imageStream, fieldManager, "layers") +} + +// ExtractImageStreamSecrets extracts the applied configuration owned by fieldManager from +// imageStream for the secrets subresource. +func ExtractImageStreamSecrets(imageStream *imagev1.ImageStream, fieldManager string) (*ImageStreamApplyConfiguration, error) { + return ExtractImageStreamFrom(imageStream, fieldManager, "secrets") +} + +// ExtractImageStreamStatus extracts the applied configuration owned by fieldManager from +// imageStream for the status subresource. +func ExtractImageStreamStatus(imageStream *imagev1.ImageStream, fieldManager string) (*ImageStreamApplyConfiguration, error) { + return ExtractImageStreamFrom(imageStream, fieldManager, "status") +} + +func (b ImageStreamApplyConfiguration) IsApplyConfiguration() {} + +// WithKind sets the Kind field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Kind field is set to the value of the last call. +func (b *ImageStreamApplyConfiguration) WithKind(value string) *ImageStreamApplyConfiguration { + b.TypeMetaApplyConfiguration.Kind = &value + return b +} + +// WithAPIVersion sets the APIVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the APIVersion field is set to the value of the last call. +func (b *ImageStreamApplyConfiguration) WithAPIVersion(value string) *ImageStreamApplyConfiguration { + b.TypeMetaApplyConfiguration.APIVersion = &value + return b +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *ImageStreamApplyConfiguration) WithName(value string) *ImageStreamApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Name = &value + return b +} + +// WithGenerateName sets the GenerateName field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the GenerateName field is set to the value of the last call. +func (b *ImageStreamApplyConfiguration) WithGenerateName(value string) *ImageStreamApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.GenerateName = &value + return b +} + +// WithNamespace sets the Namespace field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Namespace field is set to the value of the last call. +func (b *ImageStreamApplyConfiguration) WithNamespace(value string) *ImageStreamApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Namespace = &value + return b +} + +// WithUID sets the UID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the UID field is set to the value of the last call. +func (b *ImageStreamApplyConfiguration) WithUID(value types.UID) *ImageStreamApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.UID = &value + return b +} + +// WithResourceVersion sets the ResourceVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ResourceVersion field is set to the value of the last call. +func (b *ImageStreamApplyConfiguration) WithResourceVersion(value string) *ImageStreamApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.ResourceVersion = &value + return b +} + +// WithGeneration sets the Generation field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Generation field is set to the value of the last call. +func (b *ImageStreamApplyConfiguration) WithGeneration(value int64) *ImageStreamApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Generation = &value + return b +} + +// WithCreationTimestamp sets the CreationTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CreationTimestamp field is set to the value of the last call. +func (b *ImageStreamApplyConfiguration) WithCreationTimestamp(value apismetav1.Time) *ImageStreamApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.CreationTimestamp = &value + return b +} + +// WithDeletionTimestamp sets the DeletionTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionTimestamp field is set to the value of the last call. +func (b *ImageStreamApplyConfiguration) WithDeletionTimestamp(value apismetav1.Time) *ImageStreamApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionTimestamp = &value + return b +} + +// WithDeletionGracePeriodSeconds sets the DeletionGracePeriodSeconds field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionGracePeriodSeconds field is set to the value of the last call. +func (b *ImageStreamApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *ImageStreamApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionGracePeriodSeconds = &value + return b +} + +// WithLabels puts the entries into the Labels field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Labels field, +// overwriting an existing map entries in Labels field with the same key. +func (b *ImageStreamApplyConfiguration) WithLabels(entries map[string]string) *ImageStreamApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Labels == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Labels = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Labels[k] = v + } + return b +} + +// WithAnnotations puts the entries into the Annotations field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Annotations field, +// overwriting an existing map entries in Annotations field with the same key. +func (b *ImageStreamApplyConfiguration) WithAnnotations(entries map[string]string) *ImageStreamApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Annotations == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Annotations = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Annotations[k] = v + } + return b +} + +// WithOwnerReferences adds the given value to the OwnerReferences field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the OwnerReferences field. +func (b *ImageStreamApplyConfiguration) WithOwnerReferences(values ...*metav1.OwnerReferenceApplyConfiguration) *ImageStreamApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + if values[i] == nil { + panic("nil value passed to WithOwnerReferences") + } + b.ObjectMetaApplyConfiguration.OwnerReferences = append(b.ObjectMetaApplyConfiguration.OwnerReferences, *values[i]) + } + return b +} + +// WithFinalizers adds the given value to the Finalizers field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Finalizers field. +func (b *ImageStreamApplyConfiguration) WithFinalizers(values ...string) *ImageStreamApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + b.ObjectMetaApplyConfiguration.Finalizers = append(b.ObjectMetaApplyConfiguration.Finalizers, values[i]) + } + return b +} + +func (b *ImageStreamApplyConfiguration) ensureObjectMetaApplyConfigurationExists() { + if b.ObjectMetaApplyConfiguration == nil { + b.ObjectMetaApplyConfiguration = &metav1.ObjectMetaApplyConfiguration{} + } +} + +// WithSpec sets the Spec field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Spec field is set to the value of the last call. +func (b *ImageStreamApplyConfiguration) WithSpec(value *ImageStreamSpecApplyConfiguration) *ImageStreamApplyConfiguration { + b.Spec = value + return b +} + +// WithStatus sets the Status field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Status field is set to the value of the last call. +func (b *ImageStreamApplyConfiguration) WithStatus(value *ImageStreamStatusApplyConfiguration) *ImageStreamApplyConfiguration { + b.Status = value + return b +} + +// GetKind retrieves the value of the Kind field in the declarative configuration. +func (b *ImageStreamApplyConfiguration) GetKind() *string { + return b.TypeMetaApplyConfiguration.Kind +} + +// GetAPIVersion retrieves the value of the APIVersion field in the declarative configuration. +func (b *ImageStreamApplyConfiguration) GetAPIVersion() *string { + return b.TypeMetaApplyConfiguration.APIVersion +} + +// GetName retrieves the value of the Name field in the declarative configuration. +func (b *ImageStreamApplyConfiguration) GetName() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Name +} + +// GetNamespace retrieves the value of the Namespace field in the declarative configuration. +func (b *ImageStreamApplyConfiguration) GetNamespace() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Namespace +} diff --git a/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/imagestreammapping.go b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/imagestreammapping.go new file mode 100644 index 0000000000..e7f1150b96 --- /dev/null +++ b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/imagestreammapping.go @@ -0,0 +1,280 @@ +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1 + +import ( + imagev1 "github.com/openshift/api/image/v1" + internal "github.com/openshift/client-go/image/applyconfigurations/internal" + apismetav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + managedfields "k8s.io/apimachinery/pkg/util/managedfields" + metav1 "k8s.io/client-go/applyconfigurations/meta/v1" +) + +// ImageStreamMappingApplyConfiguration represents a declarative configuration of the ImageStreamMapping type for use +// with apply. +// +// ImageStreamMapping represents a mapping from a single image stream tag to a container +// image as well as the reference to the container image stream the image came from. This +// resource is used by privileged integrators to create an image resource and to associate +// it with an image stream in the status tags field. Creating an ImageStreamMapping will +// allow any user who can view the image stream to tag or pull that image, so only create +// mappings where the user has proven they have access to the image contents directly. +// The only operation supported for this resource is create and the metadata name and +// namespace should be set to the image stream containing the tag that should be updated. +// +// Compatibility level 1: Stable within a major release for a minimum of 12 months or 3 minor releases (whichever is longer). +type ImageStreamMappingApplyConfiguration struct { + metav1.TypeMetaApplyConfiguration `json:",inline"` + // metadata is the standard object's metadata. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata + *metav1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"` + // image is a container image. + Image *ImageApplyConfiguration `json:"image,omitempty"` + // tag is a string value this image can be located with inside the stream. + Tag *string `json:"tag,omitempty"` +} + +// ImageStreamMapping constructs a declarative configuration of the ImageStreamMapping type for use with +// apply. +func ImageStreamMapping(name, namespace string) *ImageStreamMappingApplyConfiguration { + b := &ImageStreamMappingApplyConfiguration{} + b.WithName(name) + b.WithNamespace(namespace) + b.WithKind("ImageStreamMapping") + b.WithAPIVersion("image.openshift.io/v1") + return b +} + +// ExtractImageStreamMappingFrom extracts the applied configuration owned by fieldManager from +// imageStreamMapping for the specified subresource. Pass an empty string for subresource to extract +// the main resource. Common subresources include "status", "scale", etc. +// imageStreamMapping must be a unmodified ImageStreamMapping API object that was retrieved from the Kubernetes API. +// ExtractImageStreamMappingFrom provides a way to perform a extract/modify-in-place/apply workflow. +// Note that an extracted apply configuration will contain fewer fields than what the fieldManager previously +// applied if another fieldManager has updated or force applied any of the previously applied fields. +func ExtractImageStreamMappingFrom(imageStreamMapping *imagev1.ImageStreamMapping, fieldManager string, subresource string) (*ImageStreamMappingApplyConfiguration, error) { + b := &ImageStreamMappingApplyConfiguration{} + err := managedfields.ExtractInto(imageStreamMapping, internal.Parser().Type("com.github.openshift.api.image.v1.ImageStreamMapping"), fieldManager, b, subresource) + if err != nil { + return nil, err + } + b.WithName(imageStreamMapping.Name) + b.WithNamespace(imageStreamMapping.Namespace) + + b.WithKind("ImageStreamMapping") + b.WithAPIVersion("image.openshift.io/v1") + return b, nil +} + +// ExtractImageStreamMapping extracts the applied configuration owned by fieldManager from +// imageStreamMapping. If no managedFields are found in imageStreamMapping for fieldManager, a +// ImageStreamMappingApplyConfiguration is returned with only the Name, Namespace (if applicable), +// APIVersion and Kind populated. It is possible that no managed fields were found for because other +// field managers have taken ownership of all the fields previously owned by fieldManager, or because +// the fieldManager never owned fields any fields. +// imageStreamMapping must be a unmodified ImageStreamMapping API object that was retrieved from the Kubernetes API. +// ExtractImageStreamMapping provides a way to perform a extract/modify-in-place/apply workflow. +// Note that an extracted apply configuration will contain fewer fields than what the fieldManager previously +// applied if another fieldManager has updated or force applied any of the previously applied fields. +func ExtractImageStreamMapping(imageStreamMapping *imagev1.ImageStreamMapping, fieldManager string) (*ImageStreamMappingApplyConfiguration, error) { + return ExtractImageStreamMappingFrom(imageStreamMapping, fieldManager, "") +} + +func (b ImageStreamMappingApplyConfiguration) IsApplyConfiguration() {} + +// WithKind sets the Kind field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Kind field is set to the value of the last call. +func (b *ImageStreamMappingApplyConfiguration) WithKind(value string) *ImageStreamMappingApplyConfiguration { + b.TypeMetaApplyConfiguration.Kind = &value + return b +} + +// WithAPIVersion sets the APIVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the APIVersion field is set to the value of the last call. +func (b *ImageStreamMappingApplyConfiguration) WithAPIVersion(value string) *ImageStreamMappingApplyConfiguration { + b.TypeMetaApplyConfiguration.APIVersion = &value + return b +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *ImageStreamMappingApplyConfiguration) WithName(value string) *ImageStreamMappingApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Name = &value + return b +} + +// WithGenerateName sets the GenerateName field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the GenerateName field is set to the value of the last call. +func (b *ImageStreamMappingApplyConfiguration) WithGenerateName(value string) *ImageStreamMappingApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.GenerateName = &value + return b +} + +// WithNamespace sets the Namespace field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Namespace field is set to the value of the last call. +func (b *ImageStreamMappingApplyConfiguration) WithNamespace(value string) *ImageStreamMappingApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Namespace = &value + return b +} + +// WithUID sets the UID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the UID field is set to the value of the last call. +func (b *ImageStreamMappingApplyConfiguration) WithUID(value types.UID) *ImageStreamMappingApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.UID = &value + return b +} + +// WithResourceVersion sets the ResourceVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ResourceVersion field is set to the value of the last call. +func (b *ImageStreamMappingApplyConfiguration) WithResourceVersion(value string) *ImageStreamMappingApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.ResourceVersion = &value + return b +} + +// WithGeneration sets the Generation field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Generation field is set to the value of the last call. +func (b *ImageStreamMappingApplyConfiguration) WithGeneration(value int64) *ImageStreamMappingApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Generation = &value + return b +} + +// WithCreationTimestamp sets the CreationTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CreationTimestamp field is set to the value of the last call. +func (b *ImageStreamMappingApplyConfiguration) WithCreationTimestamp(value apismetav1.Time) *ImageStreamMappingApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.CreationTimestamp = &value + return b +} + +// WithDeletionTimestamp sets the DeletionTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionTimestamp field is set to the value of the last call. +func (b *ImageStreamMappingApplyConfiguration) WithDeletionTimestamp(value apismetav1.Time) *ImageStreamMappingApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionTimestamp = &value + return b +} + +// WithDeletionGracePeriodSeconds sets the DeletionGracePeriodSeconds field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionGracePeriodSeconds field is set to the value of the last call. +func (b *ImageStreamMappingApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *ImageStreamMappingApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionGracePeriodSeconds = &value + return b +} + +// WithLabels puts the entries into the Labels field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Labels field, +// overwriting an existing map entries in Labels field with the same key. +func (b *ImageStreamMappingApplyConfiguration) WithLabels(entries map[string]string) *ImageStreamMappingApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Labels == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Labels = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Labels[k] = v + } + return b +} + +// WithAnnotations puts the entries into the Annotations field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Annotations field, +// overwriting an existing map entries in Annotations field with the same key. +func (b *ImageStreamMappingApplyConfiguration) WithAnnotations(entries map[string]string) *ImageStreamMappingApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Annotations == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Annotations = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Annotations[k] = v + } + return b +} + +// WithOwnerReferences adds the given value to the OwnerReferences field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the OwnerReferences field. +func (b *ImageStreamMappingApplyConfiguration) WithOwnerReferences(values ...*metav1.OwnerReferenceApplyConfiguration) *ImageStreamMappingApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + if values[i] == nil { + panic("nil value passed to WithOwnerReferences") + } + b.ObjectMetaApplyConfiguration.OwnerReferences = append(b.ObjectMetaApplyConfiguration.OwnerReferences, *values[i]) + } + return b +} + +// WithFinalizers adds the given value to the Finalizers field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Finalizers field. +func (b *ImageStreamMappingApplyConfiguration) WithFinalizers(values ...string) *ImageStreamMappingApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + b.ObjectMetaApplyConfiguration.Finalizers = append(b.ObjectMetaApplyConfiguration.Finalizers, values[i]) + } + return b +} + +func (b *ImageStreamMappingApplyConfiguration) ensureObjectMetaApplyConfigurationExists() { + if b.ObjectMetaApplyConfiguration == nil { + b.ObjectMetaApplyConfiguration = &metav1.ObjectMetaApplyConfiguration{} + } +} + +// WithImage sets the Image field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Image field is set to the value of the last call. +func (b *ImageStreamMappingApplyConfiguration) WithImage(value *ImageApplyConfiguration) *ImageStreamMappingApplyConfiguration { + b.Image = value + return b +} + +// WithTag sets the Tag field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Tag field is set to the value of the last call. +func (b *ImageStreamMappingApplyConfiguration) WithTag(value string) *ImageStreamMappingApplyConfiguration { + b.Tag = &value + return b +} + +// GetKind retrieves the value of the Kind field in the declarative configuration. +func (b *ImageStreamMappingApplyConfiguration) GetKind() *string { + return b.TypeMetaApplyConfiguration.Kind +} + +// GetAPIVersion retrieves the value of the APIVersion field in the declarative configuration. +func (b *ImageStreamMappingApplyConfiguration) GetAPIVersion() *string { + return b.TypeMetaApplyConfiguration.APIVersion +} + +// GetName retrieves the value of the Name field in the declarative configuration. +func (b *ImageStreamMappingApplyConfiguration) GetName() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Name +} + +// GetNamespace retrieves the value of the Namespace field in the declarative configuration. +func (b *ImageStreamMappingApplyConfiguration) GetNamespace() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Namespace +} diff --git a/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/imagestreamspec.go b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/imagestreamspec.go new file mode 100644 index 0000000000..15b58c8237 --- /dev/null +++ b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/imagestreamspec.go @@ -0,0 +1,53 @@ +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1 + +// ImageStreamSpecApplyConfiguration represents a declarative configuration of the ImageStreamSpec type for use +// with apply. +// +// ImageStreamSpec represents options for ImageStreams. +type ImageStreamSpecApplyConfiguration struct { + // lookupPolicy controls how other resources reference images within this namespace. + LookupPolicy *ImageLookupPolicyApplyConfiguration `json:"lookupPolicy,omitempty"` + // dockerImageRepository is optional, if specified this stream is backed by a container repository on this server + // Deprecated: This field is deprecated as of v3.7 and will be removed in a future release. + // Specify the source for the tags to be imported in each tag via the spec.tags.from reference instead. + DockerImageRepository *string `json:"dockerImageRepository,omitempty"` + // tags map arbitrary string values to specific image locators + Tags []TagReferenceApplyConfiguration `json:"tags,omitempty"` +} + +// ImageStreamSpecApplyConfiguration constructs a declarative configuration of the ImageStreamSpec type for use with +// apply. +func ImageStreamSpec() *ImageStreamSpecApplyConfiguration { + return &ImageStreamSpecApplyConfiguration{} +} + +// WithLookupPolicy sets the LookupPolicy field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the LookupPolicy field is set to the value of the last call. +func (b *ImageStreamSpecApplyConfiguration) WithLookupPolicy(value *ImageLookupPolicyApplyConfiguration) *ImageStreamSpecApplyConfiguration { + b.LookupPolicy = value + return b +} + +// WithDockerImageRepository sets the DockerImageRepository field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DockerImageRepository field is set to the value of the last call. +func (b *ImageStreamSpecApplyConfiguration) WithDockerImageRepository(value string) *ImageStreamSpecApplyConfiguration { + b.DockerImageRepository = &value + return b +} + +// WithTags adds the given value to the Tags field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Tags field. +func (b *ImageStreamSpecApplyConfiguration) WithTags(values ...*TagReferenceApplyConfiguration) *ImageStreamSpecApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithTags") + } + b.Tags = append(b.Tags, *values[i]) + } + return b +} diff --git a/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/imagestreamstatus.go b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/imagestreamstatus.go new file mode 100644 index 0000000000..2e372b095b --- /dev/null +++ b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/imagestreamstatus.go @@ -0,0 +1,55 @@ +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1 + +// ImageStreamStatusApplyConfiguration represents a declarative configuration of the ImageStreamStatus type for use +// with apply. +// +// ImageStreamStatus contains information about the state of this image stream. +type ImageStreamStatusApplyConfiguration struct { + // dockerImageRepository represents the effective location this stream may be accessed at. + // May be empty until the server determines where the repository is located + DockerImageRepository *string `json:"dockerImageRepository,omitempty"` + // publicDockerImageRepository represents the public location from where the image can + // be pulled outside the cluster. This field may be empty if the administrator + // has not exposed the integrated registry externally. + PublicDockerImageRepository *string `json:"publicDockerImageRepository,omitempty"` + // tags are a historical record of images associated with each tag. The first entry in the + // TagEvent array is the currently tagged image. + Tags []NamedTagEventListApplyConfiguration `json:"tags,omitempty"` +} + +// ImageStreamStatusApplyConfiguration constructs a declarative configuration of the ImageStreamStatus type for use with +// apply. +func ImageStreamStatus() *ImageStreamStatusApplyConfiguration { + return &ImageStreamStatusApplyConfiguration{} +} + +// WithDockerImageRepository sets the DockerImageRepository field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DockerImageRepository field is set to the value of the last call. +func (b *ImageStreamStatusApplyConfiguration) WithDockerImageRepository(value string) *ImageStreamStatusApplyConfiguration { + b.DockerImageRepository = &value + return b +} + +// WithPublicDockerImageRepository sets the PublicDockerImageRepository field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the PublicDockerImageRepository field is set to the value of the last call. +func (b *ImageStreamStatusApplyConfiguration) WithPublicDockerImageRepository(value string) *ImageStreamStatusApplyConfiguration { + b.PublicDockerImageRepository = &value + return b +} + +// WithTags adds the given value to the Tags field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Tags field. +func (b *ImageStreamStatusApplyConfiguration) WithTags(values ...*NamedTagEventListApplyConfiguration) *ImageStreamStatusApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithTags") + } + b.Tags = append(b.Tags, *values[i]) + } + return b +} diff --git a/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/namedtageventlist.go b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/namedtageventlist.go new file mode 100644 index 0000000000..85e641850e --- /dev/null +++ b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/namedtageventlist.go @@ -0,0 +1,56 @@ +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1 + +// NamedTagEventListApplyConfiguration represents a declarative configuration of the NamedTagEventList type for use +// with apply. +// +// NamedTagEventList relates a tag to its image history. +type NamedTagEventListApplyConfiguration struct { + // tag is the tag for which the history is recorded + Tag *string `json:"tag,omitempty"` + // Standard object's metadata. + Items []TagEventApplyConfiguration `json:"items,omitempty"` + // conditions is an array of conditions that apply to the tag event list. + Conditions []TagEventConditionApplyConfiguration `json:"conditions,omitempty"` +} + +// NamedTagEventListApplyConfiguration constructs a declarative configuration of the NamedTagEventList type for use with +// apply. +func NamedTagEventList() *NamedTagEventListApplyConfiguration { + return &NamedTagEventListApplyConfiguration{} +} + +// WithTag sets the Tag field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Tag field is set to the value of the last call. +func (b *NamedTagEventListApplyConfiguration) WithTag(value string) *NamedTagEventListApplyConfiguration { + b.Tag = &value + return b +} + +// WithItems adds the given value to the Items field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Items field. +func (b *NamedTagEventListApplyConfiguration) WithItems(values ...*TagEventApplyConfiguration) *NamedTagEventListApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithItems") + } + b.Items = append(b.Items, *values[i]) + } + return b +} + +// WithConditions adds the given value to the Conditions field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Conditions field. +func (b *NamedTagEventListApplyConfiguration) WithConditions(values ...*TagEventConditionApplyConfiguration) *NamedTagEventListApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithConditions") + } + b.Conditions = append(b.Conditions, *values[i]) + } + return b +} diff --git a/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/signaturecondition.go b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/signaturecondition.go new file mode 100644 index 0000000000..f7caa9b625 --- /dev/null +++ b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/signaturecondition.go @@ -0,0 +1,82 @@ +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1 + +import ( + imagev1 "github.com/openshift/api/image/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// SignatureConditionApplyConfiguration represents a declarative configuration of the SignatureCondition type for use +// with apply. +// +// SignatureCondition describes an image signature condition of particular kind at particular probe time. +type SignatureConditionApplyConfiguration struct { + // type of signature condition, Complete or Failed. + Type *imagev1.SignatureConditionType `json:"type,omitempty"` + // status of the condition, one of True, False, Unknown. + Status *corev1.ConditionStatus `json:"status,omitempty"` + // Last time the condition was checked. + LastProbeTime *metav1.Time `json:"lastProbeTime,omitempty"` + // Last time the condition transit from one status to another. + LastTransitionTime *metav1.Time `json:"lastTransitionTime,omitempty"` + // (brief) reason for the condition's last transition. + Reason *string `json:"reason,omitempty"` + // Human readable message indicating details about last transition. + Message *string `json:"message,omitempty"` +} + +// SignatureConditionApplyConfiguration constructs a declarative configuration of the SignatureCondition type for use with +// apply. +func SignatureCondition() *SignatureConditionApplyConfiguration { + return &SignatureConditionApplyConfiguration{} +} + +// WithType sets the Type field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Type field is set to the value of the last call. +func (b *SignatureConditionApplyConfiguration) WithType(value imagev1.SignatureConditionType) *SignatureConditionApplyConfiguration { + b.Type = &value + return b +} + +// WithStatus sets the Status field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Status field is set to the value of the last call. +func (b *SignatureConditionApplyConfiguration) WithStatus(value corev1.ConditionStatus) *SignatureConditionApplyConfiguration { + b.Status = &value + return b +} + +// WithLastProbeTime sets the LastProbeTime field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the LastProbeTime field is set to the value of the last call. +func (b *SignatureConditionApplyConfiguration) WithLastProbeTime(value metav1.Time) *SignatureConditionApplyConfiguration { + b.LastProbeTime = &value + return b +} + +// WithLastTransitionTime sets the LastTransitionTime field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the LastTransitionTime field is set to the value of the last call. +func (b *SignatureConditionApplyConfiguration) WithLastTransitionTime(value metav1.Time) *SignatureConditionApplyConfiguration { + b.LastTransitionTime = &value + return b +} + +// WithReason sets the Reason field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Reason field is set to the value of the last call. +func (b *SignatureConditionApplyConfiguration) WithReason(value string) *SignatureConditionApplyConfiguration { + b.Reason = &value + return b +} + +// WithMessage sets the Message field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Message field is set to the value of the last call. +func (b *SignatureConditionApplyConfiguration) WithMessage(value string) *SignatureConditionApplyConfiguration { + b.Message = &value + return b +} diff --git a/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/signaturegenericentity.go b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/signaturegenericentity.go new file mode 100644 index 0000000000..45a5c383ea --- /dev/null +++ b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/signaturegenericentity.go @@ -0,0 +1,37 @@ +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1 + +// SignatureGenericEntityApplyConfiguration represents a declarative configuration of the SignatureGenericEntity type for use +// with apply. +// +// SignatureGenericEntity holds a generic information about a person or entity who is an issuer or a subject +// of signing certificate or key. +type SignatureGenericEntityApplyConfiguration struct { + // organization name. + Organization *string `json:"organization,omitempty"` + // Common name (e.g. openshift-signing-service). + CommonName *string `json:"commonName,omitempty"` +} + +// SignatureGenericEntityApplyConfiguration constructs a declarative configuration of the SignatureGenericEntity type for use with +// apply. +func SignatureGenericEntity() *SignatureGenericEntityApplyConfiguration { + return &SignatureGenericEntityApplyConfiguration{} +} + +// WithOrganization sets the Organization field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Organization field is set to the value of the last call. +func (b *SignatureGenericEntityApplyConfiguration) WithOrganization(value string) *SignatureGenericEntityApplyConfiguration { + b.Organization = &value + return b +} + +// WithCommonName sets the CommonName field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CommonName field is set to the value of the last call. +func (b *SignatureGenericEntityApplyConfiguration) WithCommonName(value string) *SignatureGenericEntityApplyConfiguration { + b.CommonName = &value + return b +} diff --git a/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/signatureissuer.go b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/signatureissuer.go new file mode 100644 index 0000000000..45d9e3c47a --- /dev/null +++ b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/signatureissuer.go @@ -0,0 +1,33 @@ +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1 + +// SignatureIssuerApplyConfiguration represents a declarative configuration of the SignatureIssuer type for use +// with apply. +// +// SignatureIssuer holds information about an issuer of signing certificate or key. +type SignatureIssuerApplyConfiguration struct { + SignatureGenericEntityApplyConfiguration `json:",inline"` +} + +// SignatureIssuerApplyConfiguration constructs a declarative configuration of the SignatureIssuer type for use with +// apply. +func SignatureIssuer() *SignatureIssuerApplyConfiguration { + return &SignatureIssuerApplyConfiguration{} +} + +// WithOrganization sets the Organization field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Organization field is set to the value of the last call. +func (b *SignatureIssuerApplyConfiguration) WithOrganization(value string) *SignatureIssuerApplyConfiguration { + b.SignatureGenericEntityApplyConfiguration.Organization = &value + return b +} + +// WithCommonName sets the CommonName field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CommonName field is set to the value of the last call. +func (b *SignatureIssuerApplyConfiguration) WithCommonName(value string) *SignatureIssuerApplyConfiguration { + b.SignatureGenericEntityApplyConfiguration.CommonName = &value + return b +} diff --git a/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/signaturesubject.go b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/signaturesubject.go new file mode 100644 index 0000000000..055786d7ad --- /dev/null +++ b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/signaturesubject.go @@ -0,0 +1,45 @@ +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1 + +// SignatureSubjectApplyConfiguration represents a declarative configuration of the SignatureSubject type for use +// with apply. +// +// SignatureSubject holds information about a person or entity who created the signature. +type SignatureSubjectApplyConfiguration struct { + SignatureGenericEntityApplyConfiguration `json:",inline"` + // If present, it is a human readable key id of public key belonging to the subject used to verify image + // signature. It should contain at least 64 lowest bits of public key's fingerprint (e.g. + // 0x685ebe62bf278440). + PublicKeyID *string `json:"publicKeyID,omitempty"` +} + +// SignatureSubjectApplyConfiguration constructs a declarative configuration of the SignatureSubject type for use with +// apply. +func SignatureSubject() *SignatureSubjectApplyConfiguration { + return &SignatureSubjectApplyConfiguration{} +} + +// WithOrganization sets the Organization field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Organization field is set to the value of the last call. +func (b *SignatureSubjectApplyConfiguration) WithOrganization(value string) *SignatureSubjectApplyConfiguration { + b.SignatureGenericEntityApplyConfiguration.Organization = &value + return b +} + +// WithCommonName sets the CommonName field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CommonName field is set to the value of the last call. +func (b *SignatureSubjectApplyConfiguration) WithCommonName(value string) *SignatureSubjectApplyConfiguration { + b.SignatureGenericEntityApplyConfiguration.CommonName = &value + return b +} + +// WithPublicKeyID sets the PublicKeyID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the PublicKeyID field is set to the value of the last call. +func (b *SignatureSubjectApplyConfiguration) WithPublicKeyID(value string) *SignatureSubjectApplyConfiguration { + b.PublicKeyID = &value + return b +} diff --git a/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/tagevent.go b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/tagevent.go new file mode 100644 index 0000000000..695756e596 --- /dev/null +++ b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/tagevent.go @@ -0,0 +1,60 @@ +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// TagEventApplyConfiguration represents a declarative configuration of the TagEvent type for use +// with apply. +// +// TagEvent is used by ImageStreamStatus to keep a historical record of images associated with a tag. +type TagEventApplyConfiguration struct { + // created holds the time the TagEvent was created + Created *metav1.Time `json:"created,omitempty"` + // dockerImageReference is the string that can be used to pull this image + DockerImageReference *string `json:"dockerImageReference,omitempty"` + // image is the image + Image *string `json:"image,omitempty"` + // generation is the spec tag generation that resulted in this tag being updated + Generation *int64 `json:"generation,omitempty"` +} + +// TagEventApplyConfiguration constructs a declarative configuration of the TagEvent type for use with +// apply. +func TagEvent() *TagEventApplyConfiguration { + return &TagEventApplyConfiguration{} +} + +// WithCreated sets the Created field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Created field is set to the value of the last call. +func (b *TagEventApplyConfiguration) WithCreated(value metav1.Time) *TagEventApplyConfiguration { + b.Created = &value + return b +} + +// WithDockerImageReference sets the DockerImageReference field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DockerImageReference field is set to the value of the last call. +func (b *TagEventApplyConfiguration) WithDockerImageReference(value string) *TagEventApplyConfiguration { + b.DockerImageReference = &value + return b +} + +// WithImage sets the Image field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Image field is set to the value of the last call. +func (b *TagEventApplyConfiguration) WithImage(value string) *TagEventApplyConfiguration { + b.Image = &value + return b +} + +// WithGeneration sets the Generation field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Generation field is set to the value of the last call. +func (b *TagEventApplyConfiguration) WithGeneration(value int64) *TagEventApplyConfiguration { + b.Generation = &value + return b +} diff --git a/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/tageventcondition.go b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/tageventcondition.go new file mode 100644 index 0000000000..e77f02ea23 --- /dev/null +++ b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/tageventcondition.go @@ -0,0 +1,82 @@ +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1 + +import ( + imagev1 "github.com/openshift/api/image/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// TagEventConditionApplyConfiguration represents a declarative configuration of the TagEventCondition type for use +// with apply. +// +// TagEventCondition contains condition information for a tag event. +type TagEventConditionApplyConfiguration struct { + // type of tag event condition, currently only ImportSuccess + Type *imagev1.TagEventConditionType `json:"type,omitempty"` + // status of the condition, one of True, False, Unknown. + Status *corev1.ConditionStatus `json:"status,omitempty"` + // lastTransitionTime is the time the condition transitioned from one status to another. + LastTransitionTime *metav1.Time `json:"lastTransitionTime,omitempty"` + // reason is a brief machine readable explanation for the condition's last transition. + Reason *string `json:"reason,omitempty"` + // message is a human readable description of the details about last transition, complementing reason. + Message *string `json:"message,omitempty"` + // generation is the spec tag generation that this status corresponds to + Generation *int64 `json:"generation,omitempty"` +} + +// TagEventConditionApplyConfiguration constructs a declarative configuration of the TagEventCondition type for use with +// apply. +func TagEventCondition() *TagEventConditionApplyConfiguration { + return &TagEventConditionApplyConfiguration{} +} + +// WithType sets the Type field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Type field is set to the value of the last call. +func (b *TagEventConditionApplyConfiguration) WithType(value imagev1.TagEventConditionType) *TagEventConditionApplyConfiguration { + b.Type = &value + return b +} + +// WithStatus sets the Status field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Status field is set to the value of the last call. +func (b *TagEventConditionApplyConfiguration) WithStatus(value corev1.ConditionStatus) *TagEventConditionApplyConfiguration { + b.Status = &value + return b +} + +// WithLastTransitionTime sets the LastTransitionTime field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the LastTransitionTime field is set to the value of the last call. +func (b *TagEventConditionApplyConfiguration) WithLastTransitionTime(value metav1.Time) *TagEventConditionApplyConfiguration { + b.LastTransitionTime = &value + return b +} + +// WithReason sets the Reason field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Reason field is set to the value of the last call. +func (b *TagEventConditionApplyConfiguration) WithReason(value string) *TagEventConditionApplyConfiguration { + b.Reason = &value + return b +} + +// WithMessage sets the Message field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Message field is set to the value of the last call. +func (b *TagEventConditionApplyConfiguration) WithMessage(value string) *TagEventConditionApplyConfiguration { + b.Message = &value + return b +} + +// WithGeneration sets the Generation field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Generation field is set to the value of the last call. +func (b *TagEventConditionApplyConfiguration) WithGeneration(value int64) *TagEventConditionApplyConfiguration { + b.Generation = &value + return b +} diff --git a/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/tagimportpolicy.go b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/tagimportpolicy.go new file mode 100644 index 0000000000..1a38b33abb --- /dev/null +++ b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/tagimportpolicy.go @@ -0,0 +1,50 @@ +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1 + +import ( + imagev1 "github.com/openshift/api/image/v1" +) + +// TagImportPolicyApplyConfiguration represents a declarative configuration of the TagImportPolicy type for use +// with apply. +// +// TagImportPolicy controls how images related to this tag will be imported. +type TagImportPolicyApplyConfiguration struct { + // insecure is true if the server may bypass certificate verification or connect directly over HTTP during image import. + Insecure *bool `json:"insecure,omitempty"` + // scheduled indicates to the server that this tag should be periodically checked to ensure it is up to date, and imported + Scheduled *bool `json:"scheduled,omitempty"` + // importMode describes how to import an image manifest. + ImportMode *imagev1.ImportModeType `json:"importMode,omitempty"` +} + +// TagImportPolicyApplyConfiguration constructs a declarative configuration of the TagImportPolicy type for use with +// apply. +func TagImportPolicy() *TagImportPolicyApplyConfiguration { + return &TagImportPolicyApplyConfiguration{} +} + +// WithInsecure sets the Insecure field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Insecure field is set to the value of the last call. +func (b *TagImportPolicyApplyConfiguration) WithInsecure(value bool) *TagImportPolicyApplyConfiguration { + b.Insecure = &value + return b +} + +// WithScheduled sets the Scheduled field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Scheduled field is set to the value of the last call. +func (b *TagImportPolicyApplyConfiguration) WithScheduled(value bool) *TagImportPolicyApplyConfiguration { + b.Scheduled = &value + return b +} + +// WithImportMode sets the ImportMode field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ImportMode field is set to the value of the last call. +func (b *TagImportPolicyApplyConfiguration) WithImportMode(value imagev1.ImportModeType) *TagImportPolicyApplyConfiguration { + b.ImportMode = &value + return b +} diff --git a/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/tagreference.go b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/tagreference.go new file mode 100644 index 0000000000..c6710e6d96 --- /dev/null +++ b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/tagreference.go @@ -0,0 +1,105 @@ +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1 + +import ( + corev1 "k8s.io/api/core/v1" +) + +// TagReferenceApplyConfiguration represents a declarative configuration of the TagReference type for use +// with apply. +// +// TagReference specifies optional annotations for images using this tag and an optional reference to an ImageStreamTag, ImageStreamImage, or DockerImage this tag should track. +type TagReferenceApplyConfiguration struct { + // name of the tag + Name *string `json:"name,omitempty"` + // Optional; if specified, annotations that are applied to images retrieved via ImageStreamTags. + Annotations map[string]string `json:"annotations,omitempty"` + // Optional; if specified, a reference to another image that this tag should point to. Valid values + // are ImageStreamTag, ImageStreamImage, and DockerImage. ImageStreamTag references + // can only reference a tag within this same ImageStream. + From *corev1.ObjectReference `json:"from,omitempty"` + // reference states if the tag will be imported. Default value is false, which means the tag will + // be imported. + Reference *bool `json:"reference,omitempty"` + // generation is a counter that tracks mutations to the spec tag (user intent). When a tag reference + // is changed the generation is set to match the current stream generation (which is incremented every + // time spec is changed). Other processes in the system like the image importer observe that the + // generation of spec tag is newer than the generation recorded in the status and use that as a trigger + // to import the newest remote tag. To trigger a new import, clients may set this value to zero which + // will reset the generation to the latest stream generation. Legacy clients will send this value as + // nil which will be merged with the current tag generation. + Generation *int64 `json:"generation,omitempty"` + // importPolicy is information that controls how images may be imported by the server. + ImportPolicy *TagImportPolicyApplyConfiguration `json:"importPolicy,omitempty"` + // referencePolicy defines how other components should consume the image. + ReferencePolicy *TagReferencePolicyApplyConfiguration `json:"referencePolicy,omitempty"` +} + +// TagReferenceApplyConfiguration constructs a declarative configuration of the TagReference type for use with +// apply. +func TagReference() *TagReferenceApplyConfiguration { + return &TagReferenceApplyConfiguration{} +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *TagReferenceApplyConfiguration) WithName(value string) *TagReferenceApplyConfiguration { + b.Name = &value + return b +} + +// WithAnnotations puts the entries into the Annotations field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Annotations field, +// overwriting an existing map entries in Annotations field with the same key. +func (b *TagReferenceApplyConfiguration) WithAnnotations(entries map[string]string) *TagReferenceApplyConfiguration { + if b.Annotations == nil && len(entries) > 0 { + b.Annotations = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.Annotations[k] = v + } + return b +} + +// WithFrom sets the From field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the From field is set to the value of the last call. +func (b *TagReferenceApplyConfiguration) WithFrom(value corev1.ObjectReference) *TagReferenceApplyConfiguration { + b.From = &value + return b +} + +// WithReference sets the Reference field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Reference field is set to the value of the last call. +func (b *TagReferenceApplyConfiguration) WithReference(value bool) *TagReferenceApplyConfiguration { + b.Reference = &value + return b +} + +// WithGeneration sets the Generation field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Generation field is set to the value of the last call. +func (b *TagReferenceApplyConfiguration) WithGeneration(value int64) *TagReferenceApplyConfiguration { + b.Generation = &value + return b +} + +// WithImportPolicy sets the ImportPolicy field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ImportPolicy field is set to the value of the last call. +func (b *TagReferenceApplyConfiguration) WithImportPolicy(value *TagImportPolicyApplyConfiguration) *TagReferenceApplyConfiguration { + b.ImportPolicy = value + return b +} + +// WithReferencePolicy sets the ReferencePolicy field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ReferencePolicy field is set to the value of the last call. +func (b *TagReferenceApplyConfiguration) WithReferencePolicy(value *TagReferencePolicyApplyConfiguration) *TagReferenceApplyConfiguration { + b.ReferencePolicy = value + return b +} diff --git a/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/tagreferencepolicy.go b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/tagreferencepolicy.go new file mode 100644 index 0000000000..c27def9068 --- /dev/null +++ b/vendor/github.com/openshift/client-go/image/applyconfigurations/image/v1/tagreferencepolicy.go @@ -0,0 +1,39 @@ +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1 + +import ( + imagev1 "github.com/openshift/api/image/v1" +) + +// TagReferencePolicyApplyConfiguration represents a declarative configuration of the TagReferencePolicy type for use +// with apply. +// +// TagReferencePolicy describes how pull-specs for images in this image stream tag are generated when +// image change triggers in deployment configs or builds are resolved. This allows the image stream +// author to control how images are accessed. +type TagReferencePolicyApplyConfiguration struct { + // type determines how the image pull spec should be transformed when the image stream tag is used in + // deployment config triggers or new builds. The default value is `Source`, indicating the original + // location of the image should be used (if imported). The user may also specify `Local`, indicating + // that the pull spec should point to the integrated container image registry and leverage the registry's + // ability to proxy the pull to an upstream registry. `Local` allows the credentials used to pull this + // image to be managed from the image stream's namespace, so others on the platform can access a remote + // image but have no access to the remote secret. It also allows the image layers to be mirrored into + // the local registry which the images can still be pulled even if the upstream registry is unavailable. + Type *imagev1.TagReferencePolicyType `json:"type,omitempty"` +} + +// TagReferencePolicyApplyConfiguration constructs a declarative configuration of the TagReferencePolicy type for use with +// apply. +func TagReferencePolicy() *TagReferencePolicyApplyConfiguration { + return &TagReferencePolicyApplyConfiguration{} +} + +// WithType sets the Type field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Type field is set to the value of the last call. +func (b *TagReferencePolicyApplyConfiguration) WithType(value imagev1.TagReferencePolicyType) *TagReferencePolicyApplyConfiguration { + b.Type = &value + return b +} diff --git a/vendor/github.com/openshift/client-go/image/applyconfigurations/internal/internal.go b/vendor/github.com/openshift/client-go/image/applyconfigurations/internal/internal.go new file mode 100644 index 0000000000..d03b7da441 --- /dev/null +++ b/vendor/github.com/openshift/client-go/image/applyconfigurations/internal/internal.go @@ -0,0 +1,592 @@ +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package internal + +import ( + fmt "fmt" + sync "sync" + + typed "sigs.k8s.io/structured-merge-diff/v6/typed" +) + +func Parser() *typed.Parser { + parserOnce.Do(func() { + var err error + parser, err = typed.NewParser(schemaYAML) + if err != nil { + panic(fmt.Sprintf("Failed to parse schema: %v", err)) + } + }) + return parser +} + +var parserOnce sync.Once +var parser *typed.Parser +var schemaYAML = typed.YAMLObject(`types: +- name: com.github.openshift.api.image.v1.Image + map: + fields: + - name: apiVersion + type: + scalar: string + - name: dockerImageConfig + type: + scalar: string + - name: dockerImageLayers + type: + list: + elementType: + namedType: com.github.openshift.api.image.v1.ImageLayer + elementRelationship: atomic + - name: dockerImageManifest + type: + scalar: string + - name: dockerImageManifestMediaType + type: + scalar: string + - name: dockerImageManifests + type: + list: + elementType: + namedType: com.github.openshift.api.image.v1.ImageManifest + elementRelationship: atomic + - name: dockerImageMetadata + type: + namedType: __untyped_atomic_ + - name: dockerImageMetadataVersion + type: + scalar: string + - name: dockerImageReference + type: + scalar: string + - name: dockerImageSignatures + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + default: {} + - name: signatures + type: + list: + elementType: + namedType: com.github.openshift.api.image.v1.ImageSignature + elementRelationship: associative + keys: + - name +- name: com.github.openshift.api.image.v1.ImageLayer + map: + fields: + - name: mediaType + type: + scalar: string + default: "" + - name: name + type: + scalar: string + default: "" + - name: size + type: + scalar: numeric + default: 0 +- name: com.github.openshift.api.image.v1.ImageLookupPolicy + map: + fields: + - name: local + type: + scalar: boolean + default: false +- name: com.github.openshift.api.image.v1.ImageManifest + map: + fields: + - name: architecture + type: + scalar: string + default: "" + - name: digest + type: + scalar: string + default: "" + - name: manifestSize + type: + scalar: numeric + default: 0 + - name: mediaType + type: + scalar: string + default: "" + - name: os + type: + scalar: string + default: "" + - name: variant + type: + scalar: string +- name: com.github.openshift.api.image.v1.ImageSignature + map: + fields: + - name: apiVersion + type: + scalar: string + - name: conditions + type: + list: + elementType: + namedType: com.github.openshift.api.image.v1.SignatureCondition + elementRelationship: associative + keys: + - type + - name: content + type: + scalar: string + - name: created + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: imageIdentity + type: + scalar: string + - name: issuedBy + type: + namedType: com.github.openshift.api.image.v1.SignatureIssuer + - name: issuedTo + type: + namedType: com.github.openshift.api.image.v1.SignatureSubject + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + default: {} + - name: signedClaims + type: + map: + elementType: + scalar: string + - name: type + type: + scalar: string + default: "" +- name: com.github.openshift.api.image.v1.ImageStream + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + default: {} + - name: spec + type: + namedType: com.github.openshift.api.image.v1.ImageStreamSpec + default: {} + - name: status + type: + namedType: com.github.openshift.api.image.v1.ImageStreamStatus + default: {} +- name: com.github.openshift.api.image.v1.ImageStreamMapping + map: + fields: + - name: apiVersion + type: + scalar: string + - name: image + type: + namedType: com.github.openshift.api.image.v1.Image + default: {} + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + default: {} + - name: tag + type: + scalar: string + default: "" +- name: com.github.openshift.api.image.v1.ImageStreamSpec + map: + fields: + - name: dockerImageRepository + type: + scalar: string + - name: lookupPolicy + type: + namedType: com.github.openshift.api.image.v1.ImageLookupPolicy + default: {} + - name: tags + type: + list: + elementType: + namedType: com.github.openshift.api.image.v1.TagReference + elementRelationship: associative + keys: + - name +- name: com.github.openshift.api.image.v1.ImageStreamStatus + map: + fields: + - name: dockerImageRepository + type: + scalar: string + default: "" + - name: publicDockerImageRepository + type: + scalar: string + - name: tags + type: + list: + elementType: + namedType: com.github.openshift.api.image.v1.NamedTagEventList + elementRelationship: associative + keys: + - tag +- name: com.github.openshift.api.image.v1.NamedTagEventList + map: + fields: + - name: conditions + type: + list: + elementType: + namedType: com.github.openshift.api.image.v1.TagEventCondition + elementRelationship: atomic + - name: items + type: + list: + elementType: + namedType: com.github.openshift.api.image.v1.TagEvent + elementRelationship: atomic + - name: tag + type: + scalar: string + default: "" +- name: com.github.openshift.api.image.v1.SignatureCondition + map: + fields: + - name: lastProbeTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + default: "" + - name: type + type: + scalar: string + default: "" +- name: com.github.openshift.api.image.v1.SignatureIssuer + map: + fields: + - name: commonName + type: + scalar: string + - name: organization + type: + scalar: string +- name: com.github.openshift.api.image.v1.SignatureSubject + map: + fields: + - name: commonName + type: + scalar: string + - name: organization + type: + scalar: string + - name: publicKeyID + type: + scalar: string + default: "" +- name: com.github.openshift.api.image.v1.TagEvent + map: + fields: + - name: created + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: dockerImageReference + type: + scalar: string + default: "" + - name: generation + type: + scalar: numeric + default: 0 + - name: image + type: + scalar: string + default: "" +- name: com.github.openshift.api.image.v1.TagEventCondition + map: + fields: + - name: generation + type: + scalar: numeric + default: 0 + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + default: "" + - name: type + type: + scalar: string + default: "" +- name: com.github.openshift.api.image.v1.TagImportPolicy + map: + fields: + - name: importMode + type: + scalar: string + - name: insecure + type: + scalar: boolean + - name: scheduled + type: + scalar: boolean +- name: com.github.openshift.api.image.v1.TagReference + map: + fields: + - name: annotations + type: + map: + elementType: + scalar: string + - name: from + type: + namedType: io.k8s.api.core.v1.ObjectReference + - name: generation + type: + scalar: numeric + - name: importPolicy + type: + namedType: com.github.openshift.api.image.v1.TagImportPolicy + default: {} + - name: name + type: + scalar: string + default: "" + - name: reference + type: + scalar: boolean + - name: referencePolicy + type: + namedType: com.github.openshift.api.image.v1.TagReferencePolicy + default: {} +- name: com.github.openshift.api.image.v1.TagReferencePolicy + map: + fields: + - name: type + type: + scalar: string + default: "" +- name: io.k8s.api.core.v1.ObjectReference + map: + fields: + - name: apiVersion + type: + scalar: string + - name: fieldPath + type: + scalar: string + - name: kind + type: + scalar: string + - name: name + type: + scalar: string + - name: namespace + type: + scalar: string + - name: resourceVersion + type: + scalar: string + - name: uid + type: + scalar: string + elementRelationship: atomic +- name: io.k8s.apimachinery.pkg.apis.meta.v1.FieldsV1 + map: + elementType: + scalar: untyped + list: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic + map: + elementType: + namedType: __untyped_deduced_ + elementRelationship: separable +- name: io.k8s.apimachinery.pkg.apis.meta.v1.ManagedFieldsEntry + map: + fields: + - name: apiVersion + type: + scalar: string + - name: fieldsType + type: + scalar: string + - name: fieldsV1 + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.FieldsV1 + - name: manager + type: + scalar: string + - name: operation + type: + scalar: string + - name: subresource + type: + scalar: string + - name: time + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time +- name: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + map: + fields: + - name: annotations + type: + map: + elementType: + scalar: string + - name: creationTimestamp + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: deletionGracePeriodSeconds + type: + scalar: numeric + - name: deletionTimestamp + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: finalizers + type: + list: + elementType: + scalar: string + elementRelationship: associative + - name: generateName + type: + scalar: string + - name: generation + type: + scalar: numeric + - name: labels + type: + map: + elementType: + scalar: string + - name: managedFields + type: + list: + elementType: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ManagedFieldsEntry + elementRelationship: atomic + - name: name + type: + scalar: string + - name: namespace + type: + scalar: string + - name: ownerReferences + type: + list: + elementType: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.OwnerReference + elementRelationship: associative + keys: + - uid + - name: resourceVersion + type: + scalar: string + - name: selfLink + type: + scalar: string + - name: uid + type: + scalar: string +- name: io.k8s.apimachinery.pkg.apis.meta.v1.OwnerReference + map: + fields: + - name: apiVersion + type: + scalar: string + default: "" + - name: blockOwnerDeletion + type: + scalar: boolean + - name: controller + type: + scalar: boolean + - name: kind + type: + scalar: string + default: "" + - name: name + type: + scalar: string + default: "" + - name: uid + type: + scalar: string + default: "" + elementRelationship: atomic +- name: io.k8s.apimachinery.pkg.apis.meta.v1.Time + scalar: untyped +- name: io.k8s.apimachinery.pkg.runtime.RawExtension + map: + elementType: + scalar: untyped + list: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic + map: + elementType: + namedType: __untyped_deduced_ + elementRelationship: separable +- name: __untyped_atomic_ + scalar: untyped + list: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic + map: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic +- name: __untyped_deduced_ + scalar: untyped + list: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic + map: + elementType: + namedType: __untyped_deduced_ + elementRelationship: separable +`) diff --git a/vendor/github.com/openshift/client-go/image/clientset/versioned/scheme/doc.go b/vendor/github.com/openshift/client-go/image/clientset/versioned/scheme/doc.go new file mode 100644 index 0000000000..14db57a58f --- /dev/null +++ b/vendor/github.com/openshift/client-go/image/clientset/versioned/scheme/doc.go @@ -0,0 +1,4 @@ +// Code generated by client-gen. DO NOT EDIT. + +// This package contains the scheme of the automatically generated clientset. +package scheme diff --git a/vendor/github.com/openshift/client-go/image/clientset/versioned/scheme/register.go b/vendor/github.com/openshift/client-go/image/clientset/versioned/scheme/register.go new file mode 100644 index 0000000000..7765404848 --- /dev/null +++ b/vendor/github.com/openshift/client-go/image/clientset/versioned/scheme/register.go @@ -0,0 +1,40 @@ +// Code generated by client-gen. DO NOT EDIT. + +package scheme + +import ( + imagev1 "github.com/openshift/api/image/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" +) + +var Scheme = runtime.NewScheme() +var Codecs = serializer.NewCodecFactory(Scheme) +var ParameterCodec = runtime.NewParameterCodec(Scheme) +var localSchemeBuilder = runtime.SchemeBuilder{ + imagev1.AddToScheme, +} + +// AddToScheme adds all types of this clientset into the given scheme. This allows composition +// of clientsets, like in: +// +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) +// +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// +// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types +// correctly. +var AddToScheme = localSchemeBuilder.AddToScheme + +func init() { + v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) + utilruntime.Must(AddToScheme(Scheme)) +} diff --git a/vendor/github.com/openshift/client-go/image/clientset/versioned/typed/image/v1/doc.go b/vendor/github.com/openshift/client-go/image/clientset/versioned/typed/image/v1/doc.go new file mode 100644 index 0000000000..225e6b2be3 --- /dev/null +++ b/vendor/github.com/openshift/client-go/image/clientset/versioned/typed/image/v1/doc.go @@ -0,0 +1,4 @@ +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated typed clients. +package v1 diff --git a/vendor/github.com/openshift/client-go/image/clientset/versioned/typed/image/v1/generated_expansion.go b/vendor/github.com/openshift/client-go/image/clientset/versioned/typed/image/v1/generated_expansion.go new file mode 100644 index 0000000000..c495ba76e6 --- /dev/null +++ b/vendor/github.com/openshift/client-go/image/clientset/versioned/typed/image/v1/generated_expansion.go @@ -0,0 +1,19 @@ +// Code generated by client-gen. DO NOT EDIT. + +package v1 + +type ImageExpansion interface{} + +type ImageSignatureExpansion interface{} + +type ImageStreamExpansion interface{} + +type ImageStreamImageExpansion interface{} + +type ImageStreamImportExpansion interface{} + +type ImageStreamMappingExpansion interface{} + +type ImageStreamTagExpansion interface{} + +type ImageTagExpansion interface{} diff --git a/vendor/github.com/openshift/client-go/image/clientset/versioned/typed/image/v1/image.go b/vendor/github.com/openshift/client-go/image/clientset/versioned/typed/image/v1/image.go new file mode 100644 index 0000000000..6976e8debc --- /dev/null +++ b/vendor/github.com/openshift/client-go/image/clientset/versioned/typed/image/v1/image.go @@ -0,0 +1,54 @@ +// Code generated by client-gen. DO NOT EDIT. + +package v1 + +import ( + context "context" + + imagev1 "github.com/openshift/api/image/v1" + applyconfigurationsimagev1 "github.com/openshift/client-go/image/applyconfigurations/image/v1" + scheme "github.com/openshift/client-go/image/clientset/versioned/scheme" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + gentype "k8s.io/client-go/gentype" +) + +// ImagesGetter has a method to return a ImageInterface. +// A group's client should implement this interface. +type ImagesGetter interface { + Images() ImageInterface +} + +// ImageInterface has methods to work with Image resources. +type ImageInterface interface { + Create(ctx context.Context, image *imagev1.Image, opts metav1.CreateOptions) (*imagev1.Image, error) + Update(ctx context.Context, image *imagev1.Image, opts metav1.UpdateOptions) (*imagev1.Image, error) + Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error + Get(ctx context.Context, name string, opts metav1.GetOptions) (*imagev1.Image, error) + List(ctx context.Context, opts metav1.ListOptions) (*imagev1.ImageList, error) + Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *imagev1.Image, err error) + Apply(ctx context.Context, image *applyconfigurationsimagev1.ImageApplyConfiguration, opts metav1.ApplyOptions) (result *imagev1.Image, err error) + ImageExpansion +} + +// images implements ImageInterface +type images struct { + *gentype.ClientWithListAndApply[*imagev1.Image, *imagev1.ImageList, *applyconfigurationsimagev1.ImageApplyConfiguration] +} + +// newImages returns a Images +func newImages(c *ImageV1Client) *images { + return &images{ + gentype.NewClientWithListAndApply[*imagev1.Image, *imagev1.ImageList, *applyconfigurationsimagev1.ImageApplyConfiguration]( + "images", + c.RESTClient(), + scheme.ParameterCodec, + "", + func() *imagev1.Image { return &imagev1.Image{} }, + func() *imagev1.ImageList { return &imagev1.ImageList{} }, + ), + } +} diff --git a/vendor/github.com/openshift/client-go/image/clientset/versioned/typed/image/v1/image_client.go b/vendor/github.com/openshift/client-go/image/clientset/versioned/typed/image/v1/image_client.go new file mode 100644 index 0000000000..85be450238 --- /dev/null +++ b/vendor/github.com/openshift/client-go/image/clientset/versioned/typed/image/v1/image_client.go @@ -0,0 +1,120 @@ +// Code generated by client-gen. DO NOT EDIT. + +package v1 + +import ( + http "net/http" + + imagev1 "github.com/openshift/api/image/v1" + scheme "github.com/openshift/client-go/image/clientset/versioned/scheme" + rest "k8s.io/client-go/rest" +) + +type ImageV1Interface interface { + RESTClient() rest.Interface + ImagesGetter + ImageSignaturesGetter + ImageStreamsGetter + ImageStreamImagesGetter + ImageStreamImportsGetter + ImageStreamMappingsGetter + ImageStreamTagsGetter + ImageTagsGetter +} + +// ImageV1Client is used to interact with features provided by the image.openshift.io group. +type ImageV1Client struct { + restClient rest.Interface +} + +func (c *ImageV1Client) Images() ImageInterface { + return newImages(c) +} + +func (c *ImageV1Client) ImageSignatures() ImageSignatureInterface { + return newImageSignatures(c) +} + +func (c *ImageV1Client) ImageStreams(namespace string) ImageStreamInterface { + return newImageStreams(c, namespace) +} + +func (c *ImageV1Client) ImageStreamImages(namespace string) ImageStreamImageInterface { + return newImageStreamImages(c, namespace) +} + +func (c *ImageV1Client) ImageStreamImports(namespace string) ImageStreamImportInterface { + return newImageStreamImports(c, namespace) +} + +func (c *ImageV1Client) ImageStreamMappings(namespace string) ImageStreamMappingInterface { + return newImageStreamMappings(c, namespace) +} + +func (c *ImageV1Client) ImageStreamTags(namespace string) ImageStreamTagInterface { + return newImageStreamTags(c, namespace) +} + +func (c *ImageV1Client) ImageTags(namespace string) ImageTagInterface { + return newImageTags(c, namespace) +} + +// NewForConfig creates a new ImageV1Client for the given config. +// NewForConfig is equivalent to NewForConfigAndClient(c, httpClient), +// where httpClient was generated with rest.HTTPClientFor(c). +func NewForConfig(c *rest.Config) (*ImageV1Client, error) { + config := *c + setConfigDefaults(&config) + httpClient, err := rest.HTTPClientFor(&config) + if err != nil { + return nil, err + } + return NewForConfigAndClient(&config, httpClient) +} + +// NewForConfigAndClient creates a new ImageV1Client for the given config and http client. +// Note the http client provided takes precedence over the configured transport values. +func NewForConfigAndClient(c *rest.Config, h *http.Client) (*ImageV1Client, error) { + config := *c + setConfigDefaults(&config) + client, err := rest.RESTClientForConfigAndClient(&config, h) + if err != nil { + return nil, err + } + return &ImageV1Client{client}, nil +} + +// NewForConfigOrDie creates a new ImageV1Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *ImageV1Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new ImageV1Client for the given RESTClient. +func New(c rest.Interface) *ImageV1Client { + return &ImageV1Client{c} +} + +func setConfigDefaults(config *rest.Config) { + gv := imagev1.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = rest.CodecFactoryForGeneratedClient(scheme.Scheme, scheme.Codecs).WithoutConversion() + + if config.UserAgent == "" { + config.UserAgent = rest.DefaultKubernetesUserAgent() + } +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *ImageV1Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/vendor/github.com/openshift/client-go/image/clientset/versioned/typed/image/v1/imagesignature.go b/vendor/github.com/openshift/client-go/image/clientset/versioned/typed/image/v1/imagesignature.go new file mode 100644 index 0000000000..a53b63c62f --- /dev/null +++ b/vendor/github.com/openshift/client-go/image/clientset/versioned/typed/image/v1/imagesignature.go @@ -0,0 +1,43 @@ +// Code generated by client-gen. DO NOT EDIT. + +package v1 + +import ( + context "context" + + imagev1 "github.com/openshift/api/image/v1" + scheme "github.com/openshift/client-go/image/clientset/versioned/scheme" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gentype "k8s.io/client-go/gentype" +) + +// ImageSignaturesGetter has a method to return a ImageSignatureInterface. +// A group's client should implement this interface. +type ImageSignaturesGetter interface { + ImageSignatures() ImageSignatureInterface +} + +// ImageSignatureInterface has methods to work with ImageSignature resources. +type ImageSignatureInterface interface { + Create(ctx context.Context, imageSignature *imagev1.ImageSignature, opts metav1.CreateOptions) (*imagev1.ImageSignature, error) + Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error + ImageSignatureExpansion +} + +// imageSignatures implements ImageSignatureInterface +type imageSignatures struct { + *gentype.Client[*imagev1.ImageSignature] +} + +// newImageSignatures returns a ImageSignatures +func newImageSignatures(c *ImageV1Client) *imageSignatures { + return &imageSignatures{ + gentype.NewClient[*imagev1.ImageSignature]( + "imagesignatures", + c.RESTClient(), + scheme.ParameterCodec, + "", + func() *imagev1.ImageSignature { return &imagev1.ImageSignature{} }, + ), + } +} diff --git a/vendor/github.com/openshift/client-go/image/clientset/versioned/typed/image/v1/imagestream.go b/vendor/github.com/openshift/client-go/image/clientset/versioned/typed/image/v1/imagestream.go new file mode 100644 index 0000000000..90fc21df2d --- /dev/null +++ b/vendor/github.com/openshift/client-go/image/clientset/versioned/typed/image/v1/imagestream.go @@ -0,0 +1,89 @@ +// Code generated by client-gen. DO NOT EDIT. + +package v1 + +import ( + context "context" + + imagev1 "github.com/openshift/api/image/v1" + applyconfigurationsimagev1 "github.com/openshift/client-go/image/applyconfigurations/image/v1" + scheme "github.com/openshift/client-go/image/clientset/versioned/scheme" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + gentype "k8s.io/client-go/gentype" +) + +// ImageStreamsGetter has a method to return a ImageStreamInterface. +// A group's client should implement this interface. +type ImageStreamsGetter interface { + ImageStreams(namespace string) ImageStreamInterface +} + +// ImageStreamInterface has methods to work with ImageStream resources. +type ImageStreamInterface interface { + Create(ctx context.Context, imageStream *imagev1.ImageStream, opts metav1.CreateOptions) (*imagev1.ImageStream, error) + Update(ctx context.Context, imageStream *imagev1.ImageStream, opts metav1.UpdateOptions) (*imagev1.ImageStream, error) + // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + UpdateStatus(ctx context.Context, imageStream *imagev1.ImageStream, opts metav1.UpdateOptions) (*imagev1.ImageStream, error) + Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error + Get(ctx context.Context, name string, opts metav1.GetOptions) (*imagev1.ImageStream, error) + List(ctx context.Context, opts metav1.ListOptions) (*imagev1.ImageStreamList, error) + Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *imagev1.ImageStream, err error) + Apply(ctx context.Context, imageStream *applyconfigurationsimagev1.ImageStreamApplyConfiguration, opts metav1.ApplyOptions) (result *imagev1.ImageStream, err error) + // Add a +genclient:noStatus comment above the type to avoid generating ApplyStatus(). + ApplyStatus(ctx context.Context, imageStream *applyconfigurationsimagev1.ImageStreamApplyConfiguration, opts metav1.ApplyOptions) (result *imagev1.ImageStream, err error) + Secrets(ctx context.Context, imageStreamName string, options metav1.GetOptions) (*imagev1.SecretList, error) + Layers(ctx context.Context, imageStreamName string, options metav1.GetOptions) (*imagev1.ImageStreamLayers, error) + + ImageStreamExpansion +} + +// imageStreams implements ImageStreamInterface +type imageStreams struct { + *gentype.ClientWithListAndApply[*imagev1.ImageStream, *imagev1.ImageStreamList, *applyconfigurationsimagev1.ImageStreamApplyConfiguration] +} + +// newImageStreams returns a ImageStreams +func newImageStreams(c *ImageV1Client, namespace string) *imageStreams { + return &imageStreams{ + gentype.NewClientWithListAndApply[*imagev1.ImageStream, *imagev1.ImageStreamList, *applyconfigurationsimagev1.ImageStreamApplyConfiguration]( + "imagestreams", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *imagev1.ImageStream { return &imagev1.ImageStream{} }, + func() *imagev1.ImageStreamList { return &imagev1.ImageStreamList{} }, + ), + } +} + +// Secrets takes name of the imageStream, and returns the corresponding imagev1.SecretList object, and an error if there is any. +func (c *imageStreams) Secrets(ctx context.Context, imageStreamName string, options metav1.GetOptions) (result *imagev1.SecretList, err error) { + result = &imagev1.SecretList{} + err = c.GetClient().Get(). + Namespace(c.GetNamespace()). + Resource("imagestreams"). + Name(imageStreamName). + SubResource("secrets"). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// Layers takes name of the imageStream, and returns the corresponding imagev1.ImageStreamLayers object, and an error if there is any. +func (c *imageStreams) Layers(ctx context.Context, imageStreamName string, options metav1.GetOptions) (result *imagev1.ImageStreamLayers, err error) { + result = &imagev1.ImageStreamLayers{} + err = c.GetClient().Get(). + Namespace(c.GetNamespace()). + Resource("imagestreams"). + Name(imageStreamName). + SubResource("layers"). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} diff --git a/vendor/github.com/openshift/client-go/image/clientset/versioned/typed/image/v1/imagestreamimage.go b/vendor/github.com/openshift/client-go/image/clientset/versioned/typed/image/v1/imagestreamimage.go new file mode 100644 index 0000000000..ea329220bb --- /dev/null +++ b/vendor/github.com/openshift/client-go/image/clientset/versioned/typed/image/v1/imagestreamimage.go @@ -0,0 +1,42 @@ +// Code generated by client-gen. DO NOT EDIT. + +package v1 + +import ( + context "context" + + imagev1 "github.com/openshift/api/image/v1" + scheme "github.com/openshift/client-go/image/clientset/versioned/scheme" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gentype "k8s.io/client-go/gentype" +) + +// ImageStreamImagesGetter has a method to return a ImageStreamImageInterface. +// A group's client should implement this interface. +type ImageStreamImagesGetter interface { + ImageStreamImages(namespace string) ImageStreamImageInterface +} + +// ImageStreamImageInterface has methods to work with ImageStreamImage resources. +type ImageStreamImageInterface interface { + Get(ctx context.Context, name string, opts metav1.GetOptions) (*imagev1.ImageStreamImage, error) + ImageStreamImageExpansion +} + +// imageStreamImages implements ImageStreamImageInterface +type imageStreamImages struct { + *gentype.Client[*imagev1.ImageStreamImage] +} + +// newImageStreamImages returns a ImageStreamImages +func newImageStreamImages(c *ImageV1Client, namespace string) *imageStreamImages { + return &imageStreamImages{ + gentype.NewClient[*imagev1.ImageStreamImage]( + "imagestreamimages", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *imagev1.ImageStreamImage { return &imagev1.ImageStreamImage{} }, + ), + } +} diff --git a/vendor/github.com/openshift/client-go/image/clientset/versioned/typed/image/v1/imagestreamimport.go b/vendor/github.com/openshift/client-go/image/clientset/versioned/typed/image/v1/imagestreamimport.go new file mode 100644 index 0000000000..7fbd420a40 --- /dev/null +++ b/vendor/github.com/openshift/client-go/image/clientset/versioned/typed/image/v1/imagestreamimport.go @@ -0,0 +1,42 @@ +// Code generated by client-gen. DO NOT EDIT. + +package v1 + +import ( + context "context" + + imagev1 "github.com/openshift/api/image/v1" + scheme "github.com/openshift/client-go/image/clientset/versioned/scheme" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gentype "k8s.io/client-go/gentype" +) + +// ImageStreamImportsGetter has a method to return a ImageStreamImportInterface. +// A group's client should implement this interface. +type ImageStreamImportsGetter interface { + ImageStreamImports(namespace string) ImageStreamImportInterface +} + +// ImageStreamImportInterface has methods to work with ImageStreamImport resources. +type ImageStreamImportInterface interface { + Create(ctx context.Context, imageStreamImport *imagev1.ImageStreamImport, opts metav1.CreateOptions) (*imagev1.ImageStreamImport, error) + ImageStreamImportExpansion +} + +// imageStreamImports implements ImageStreamImportInterface +type imageStreamImports struct { + *gentype.Client[*imagev1.ImageStreamImport] +} + +// newImageStreamImports returns a ImageStreamImports +func newImageStreamImports(c *ImageV1Client, namespace string) *imageStreamImports { + return &imageStreamImports{ + gentype.NewClient[*imagev1.ImageStreamImport]( + "imagestreamimports", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *imagev1.ImageStreamImport { return &imagev1.ImageStreamImport{} }, + ), + } +} diff --git a/vendor/github.com/openshift/client-go/image/clientset/versioned/typed/image/v1/imagestreammapping.go b/vendor/github.com/openshift/client-go/image/clientset/versioned/typed/image/v1/imagestreammapping.go new file mode 100644 index 0000000000..eae9d77dfd --- /dev/null +++ b/vendor/github.com/openshift/client-go/image/clientset/versioned/typed/image/v1/imagestreammapping.go @@ -0,0 +1,58 @@ +// Code generated by client-gen. DO NOT EDIT. + +package v1 + +import ( + context "context" + + apiimagev1 "github.com/openshift/api/image/v1" + imagev1 "github.com/openshift/client-go/image/applyconfigurations/image/v1" + scheme "github.com/openshift/client-go/image/clientset/versioned/scheme" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gentype "k8s.io/client-go/gentype" +) + +// ImageStreamMappingsGetter has a method to return a ImageStreamMappingInterface. +// A group's client should implement this interface. +type ImageStreamMappingsGetter interface { + ImageStreamMappings(namespace string) ImageStreamMappingInterface +} + +// ImageStreamMappingInterface has methods to work with ImageStreamMapping resources. +type ImageStreamMappingInterface interface { + Apply(ctx context.Context, imageStreamMapping *imagev1.ImageStreamMappingApplyConfiguration, opts metav1.ApplyOptions) (result *apiimagev1.ImageStreamMapping, err error) + Create(ctx context.Context, imageStreamMapping *apiimagev1.ImageStreamMapping, opts metav1.CreateOptions) (*metav1.Status, error) + + ImageStreamMappingExpansion +} + +// imageStreamMappings implements ImageStreamMappingInterface +type imageStreamMappings struct { + *gentype.ClientWithApply[*apiimagev1.ImageStreamMapping, *imagev1.ImageStreamMappingApplyConfiguration] +} + +// newImageStreamMappings returns a ImageStreamMappings +func newImageStreamMappings(c *ImageV1Client, namespace string) *imageStreamMappings { + return &imageStreamMappings{ + gentype.NewClientWithApply[*apiimagev1.ImageStreamMapping, *imagev1.ImageStreamMappingApplyConfiguration]( + "imagestreammappings", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *apiimagev1.ImageStreamMapping { return &apiimagev1.ImageStreamMapping{} }, + ), + } +} + +// Create takes the representation of a imageStreamMapping and creates it. Returns the server's representation of the status, and an error, if there is any. +func (c *imageStreamMappings) Create(ctx context.Context, imageStreamMapping *apiimagev1.ImageStreamMapping, opts metav1.CreateOptions) (result *metav1.Status, err error) { + result = &metav1.Status{} + err = c.GetClient().Post(). + Namespace(c.GetNamespace()). + Resource("imagestreammappings"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(imageStreamMapping). + Do(ctx). + Into(result) + return +} diff --git a/vendor/github.com/openshift/client-go/image/clientset/versioned/typed/image/v1/imagestreamtag.go b/vendor/github.com/openshift/client-go/image/clientset/versioned/typed/image/v1/imagestreamtag.go new file mode 100644 index 0000000000..2f09ab86cb --- /dev/null +++ b/vendor/github.com/openshift/client-go/image/clientset/versioned/typed/image/v1/imagestreamtag.go @@ -0,0 +1,47 @@ +// Code generated by client-gen. DO NOT EDIT. + +package v1 + +import ( + context "context" + + imagev1 "github.com/openshift/api/image/v1" + scheme "github.com/openshift/client-go/image/clientset/versioned/scheme" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gentype "k8s.io/client-go/gentype" +) + +// ImageStreamTagsGetter has a method to return a ImageStreamTagInterface. +// A group's client should implement this interface. +type ImageStreamTagsGetter interface { + ImageStreamTags(namespace string) ImageStreamTagInterface +} + +// ImageStreamTagInterface has methods to work with ImageStreamTag resources. +type ImageStreamTagInterface interface { + Create(ctx context.Context, imageStreamTag *imagev1.ImageStreamTag, opts metav1.CreateOptions) (*imagev1.ImageStreamTag, error) + Update(ctx context.Context, imageStreamTag *imagev1.ImageStreamTag, opts metav1.UpdateOptions) (*imagev1.ImageStreamTag, error) + Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error + Get(ctx context.Context, name string, opts metav1.GetOptions) (*imagev1.ImageStreamTag, error) + List(ctx context.Context, opts metav1.ListOptions) (*imagev1.ImageStreamTagList, error) + ImageStreamTagExpansion +} + +// imageStreamTags implements ImageStreamTagInterface +type imageStreamTags struct { + *gentype.ClientWithList[*imagev1.ImageStreamTag, *imagev1.ImageStreamTagList] +} + +// newImageStreamTags returns a ImageStreamTags +func newImageStreamTags(c *ImageV1Client, namespace string) *imageStreamTags { + return &imageStreamTags{ + gentype.NewClientWithList[*imagev1.ImageStreamTag, *imagev1.ImageStreamTagList]( + "imagestreamtags", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *imagev1.ImageStreamTag { return &imagev1.ImageStreamTag{} }, + func() *imagev1.ImageStreamTagList { return &imagev1.ImageStreamTagList{} }, + ), + } +} diff --git a/vendor/github.com/openshift/client-go/image/clientset/versioned/typed/image/v1/imagetag.go b/vendor/github.com/openshift/client-go/image/clientset/versioned/typed/image/v1/imagetag.go new file mode 100644 index 0000000000..1d69b558d4 --- /dev/null +++ b/vendor/github.com/openshift/client-go/image/clientset/versioned/typed/image/v1/imagetag.go @@ -0,0 +1,47 @@ +// Code generated by client-gen. DO NOT EDIT. + +package v1 + +import ( + context "context" + + imagev1 "github.com/openshift/api/image/v1" + scheme "github.com/openshift/client-go/image/clientset/versioned/scheme" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gentype "k8s.io/client-go/gentype" +) + +// ImageTagsGetter has a method to return a ImageTagInterface. +// A group's client should implement this interface. +type ImageTagsGetter interface { + ImageTags(namespace string) ImageTagInterface +} + +// ImageTagInterface has methods to work with ImageTag resources. +type ImageTagInterface interface { + Create(ctx context.Context, imageTag *imagev1.ImageTag, opts metav1.CreateOptions) (*imagev1.ImageTag, error) + Update(ctx context.Context, imageTag *imagev1.ImageTag, opts metav1.UpdateOptions) (*imagev1.ImageTag, error) + Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error + Get(ctx context.Context, name string, opts metav1.GetOptions) (*imagev1.ImageTag, error) + List(ctx context.Context, opts metav1.ListOptions) (*imagev1.ImageTagList, error) + ImageTagExpansion +} + +// imageTags implements ImageTagInterface +type imageTags struct { + *gentype.ClientWithList[*imagev1.ImageTag, *imagev1.ImageTagList] +} + +// newImageTags returns a ImageTags +func newImageTags(c *ImageV1Client, namespace string) *imageTags { + return &imageTags{ + gentype.NewClientWithList[*imagev1.ImageTag, *imagev1.ImageTagList]( + "imagetags", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *imagev1.ImageTag { return &imagev1.ImageTag{} }, + func() *imagev1.ImageTagList { return &imagev1.ImageTagList{} }, + ), + } +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 3efb3830b0..c2a254d4e0 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -370,6 +370,10 @@ github.com/openshift/client-go/config/informers/externalversions/internalinterfa github.com/openshift/client-go/config/listers/config/v1 github.com/openshift/client-go/config/listers/config/v1alpha1 github.com/openshift/client-go/config/listers/config/v1alpha2 +github.com/openshift/client-go/image/applyconfigurations/image/v1 +github.com/openshift/client-go/image/applyconfigurations/internal +github.com/openshift/client-go/image/clientset/versioned/scheme +github.com/openshift/client-go/image/clientset/versioned/typed/image/v1 github.com/openshift/client-go/oauth/applyconfigurations github.com/openshift/client-go/oauth/applyconfigurations/internal github.com/openshift/client-go/oauth/applyconfigurations/oauth/v1