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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 48 additions & 1 deletion cmd/cluster-authentication-operator-tests-ext/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ import (
"github.com/openshift/cluster-authentication-operator/pkg/version"

_ "github.com/openshift/cluster-authentication-operator/test/e2e"
_ "github.com/openshift/cluster-authentication-operator/test/e2e-encryption"
_ "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"
)
Expand Down Expand Up @@ -74,11 +78,12 @@ func prepareOperatorTestsRegistry() (*oteextension.Registry, error) {
// 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.
// Disruptive tests are excluded as they run in the disruptive suite instead.
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("[Serial]") && !name.contains("[Disruptive]") && (name.contains("[Operator]") || name.contains("[OIDC]") || name.contains("[Templates]") || name.contains("[Tokens]"))`,
},
})

Expand All @@ -92,6 +97,36 @@ func prepareOperatorTestsRegistry() (*oteextension.Registry, error) {
ClusterStability: oteextension.ClusterStabilityDisruptive,
})

// ClusterStability set to Disruptive: encryption tests trigger API server rollouts.
extension.AddSuite(oteextension.Suite{
Name: "openshift/cluster-authentication-operator/operator-encryption/serial",
Parallelism: 1,
ClusterStability: oteextension.ClusterStabilityDisruptive,
Qualifiers: []string{
`name.contains("[Encryption]") && name.contains("[Serial]") && !name.contains("Rotation") && !name.contains("Perf") && !name.contains("KMS")`,
},
})

// 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")`,
},
})

// ClusterStability set to Disruptive: encryption perf tests trigger API server rollouts.
extension.AddSuite(oteextension.Suite{
Name: "openshift/cluster-authentication-operator/operator-encryption-perf/serial",
Parallelism: 1,
ClusterStability: oteextension.ClusterStabilityDisruptive,
Qualifiers: []string{
`name.contains("[Encryption]") && name.contains("[Serial]") && name.contains("Perf")`,
},
})

// The following suite runs KMS encryption tests.
extension.AddSuite(oteextension.Suite{
Name: "openshift/cluster-authentication-operator/encryption-kms",
Expand All @@ -101,6 +136,18 @@ func prepareOperatorTestsRegistry() (*oteextension.Registry, error) {
},
})

// Register external images used by OIDC tests
extension.RegisterImage(oteextension.Image{
Registry: "quay.io",
Name: "keycloak/keycloak",
Version: "25.0",
})
extension.RegisterImage(oteextension.Image{
Registry: "docker.io",
Name: "gitlab/gitlab-ce",
Version: "13.8.4-ce.0",
})

specs, err := oteginkgo.BuildExtensionTestSpecsFromOpenShiftGinkgoSuite()
if err != nil {
return nil, fmt.Errorf("couldn't build extension test specs from ginkgo: %w", err)
Expand Down
117 changes: 117 additions & 0 deletions test/e2e-encryption-perf/encryption_perf.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package e2e_encryption_perf

import (
"context"
"errors"
"fmt"
"testing"
"time"

g "github.com/onsi/ginkgo/v2"
"github.com/stretchr/testify/require"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"

configv1 "github.com/openshift/api/config/v1"
oauthapiv1 "github.com/openshift/api/oauth/v1"
operatorv1 "github.com/openshift/api/operator/v1"
oauthclient "github.com/openshift/client-go/oauth/clientset/versioned/typed/oauth/v1"
operatorlibrary "github.com/openshift/cluster-authentication-operator/test/library"
operatorencryption "github.com/openshift/cluster-authentication-operator/test/library/encryption"
library "github.com/openshift/library-go/test/library/encryption"
)

const (
tokenStatsKey = "created oauthaccesstokens"
)

var _ = g.Describe("[sig-auth] authentication operator", func() {
g.It("[Encryption][Serial] TestPerfEncryptionTypeAESCBC", func() {
testPerfEncryptionTypeAESCBC(g.GinkgoTB())
})
})

func testPerfEncryptionTypeAESCBC(tt testing.TB) {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Minute)
tt.Cleanup(cancel)
clientSet := getPerfClients(tt)
operatorlibrary.TestPerfEncryption(tt, library.PerfScenario{
BasicScenario: library.BasicScenario{
Namespace: "openshift-config-managed",
LabelSelector: "encryption.apiserver.operator.openshift.io/component" + "=" + "openshift-oauth-apiserver",
EncryptionConfigSecretName: fmt.Sprintf("encryption-config-%s", "openshift-oauth-apiserver"),
EncryptionConfigSecretNamespace: "openshift-config-managed",
OperatorNamespace: "openshift-authentication-operator",
TargetGRs: operatorencryption.DefaultTargetGRs,
AssertFunc: operatorencryption.AssertTokens,
},
GetOperatorConditionsFunc: func(t testing.TB) ([]operatorv1.OperatorCondition, error) {
apiServerOperator, err := clientSet.OperatorClient.Get(ctx, "cluster", metav1.GetOptions{})
if err != nil {
return nil, err
}
return apiServerOperator.Status.Conditions, nil
},
AssertDBPopulatedFunc: func(t testing.TB, errorStore map[string]int, statStore map[string]int) {
tokenCount, ok := statStore[tokenStatsKey]
if !ok {
err := errors.New("missing oauth access tokens count stats, can't continue the test")
require.NoError(t, err)
}
if tokenCount < 14000 {
err := fmt.Errorf("expected to create at least 14000 tokens but %d were created", tokenCount)
require.NoError(t, err)
}
t.Logf("Created %d access tokens", tokenCount)
},
AssertMigrationTime: func(t testing.TB, migrationTime time.Duration) {
t.Logf("migration took %v", migrationTime)
expectedMigrationTime := 10 * time.Minute
if migrationTime > expectedMigrationTime {
t.Errorf("migration took too long (%v), expected it to take no more than %v", migrationTime, expectedMigrationTime)
}
},
DBLoaderWorkers: 3,
DBLoaderFunc: library.DBLoaderRepeat(1, false,
library.DBLoaderRepeatParallel(5010, 50, false, createAccessTokenWrapper(ctx, clientSet.TokenClient), reportSecret)),
EncryptionProvider: library.EncryptionProvider{
APIServerEncryption: configv1.APIServerEncryption{Type: configv1.EncryptionTypeAESCBC},
},
})
}

func createAccessTokenWrapper(ctx context.Context, tokenClient oauthclient.OAuthAccessTokensGetter) library.DBLoaderFuncType {
return func(_ kubernetes.Interface, namespace string, errorCollector func(error), statsCollector func(string)) error {
_, tokenNameHash := operatorlibrary.GenerateOAuthTokenPair()
token := &oauthapiv1.OAuthAccessToken{
ObjectMeta: metav1.ObjectMeta{
Name: tokenNameHash,
},
RefreshToken: "I have no special talents. I am only passionately curious",
UserName: "kube:admin",
Scopes: []string{"user:full"},
RedirectURI: "redirect.me.to.token.of.life",
ClientName: "console",
UserUID: "non-existing-user-id",
}
_, err := tokenClient.OAuthAccessTokens().Create(ctx, token, metav1.CreateOptions{})
return err
}
}

func reportSecret(_ kubernetes.Interface, _ string, _ func(error), statsCollector func(string)) error {
statsCollector(tokenStatsKey)
return nil
}

func getPerfClients(t testing.TB) operatorencryption.ClientSet {
t.Helper()

kubeConfig := operatorlibrary.NewClientConfigForTest(t)

kubeConfig.QPS = 300
kubeConfig.Burst = 600

return operatorencryption.GetClientsFor(t, kubeConfig)
}
107 changes: 6 additions & 101 deletions test/e2e-encryption-perf/encryption_perf_test.go
Original file line number Diff line number Diff line change
@@ -1,109 +1,14 @@
package e2e_encryption_perf

import (
"context"
"errors"
"fmt"
"testing"
"time"

"github.com/stretchr/testify/require"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"

configv1 "github.com/openshift/api/config/v1"
oauthapiv1 "github.com/openshift/api/oauth/v1"
operatorv1 "github.com/openshift/api/operator/v1"
oauthclient "github.com/openshift/client-go/oauth/clientset/versioned/typed/oauth/v1"
operatorlibrary "github.com/openshift/cluster-authentication-operator/test/library"
operatorencryption "github.com/openshift/cluster-authentication-operator/test/library/encryption"
library "github.com/openshift/library-go/test/library/encryption"
)

const (
tokenStatsKey = "created oauthaccesstokens"
)

// 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-aws-operator-encryption-perf-serial-ote job.
// Eventually all tests will be run only as part of the OTE framework.
func TestPerfEncryptionTypeAESCBC(tt *testing.T) {
ctx := context.TODO()
clientSet := getPerfClients(tt)
library.TestPerfEncryption(tt.Context(), tt, library.PerfScenario{
BasicScenario: library.BasicScenario{
Namespace: "openshift-config-managed",
LabelSelector: "encryption.apiserver.operator.openshift.io/component" + "=" + "openshift-oauth-apiserver",
EncryptionConfigSecretName: fmt.Sprintf("encryption-config-%s", "openshift-oauth-apiserver"),
EncryptionConfigSecretNamespace: "openshift-config-managed",
OperatorNamespace: "openshift-authentication-operator",
TargetGRs: operatorencryption.DefaultTargetGRs,
AssertFunc: operatorencryption.AssertTokens,
},
GetOperatorConditionsFunc: func(t testing.TB) ([]operatorv1.OperatorCondition, error) {
apiServerOperator, err := clientSet.OperatorClient.Get(ctx, "cluster", metav1.GetOptions{})
if err != nil {
return nil, err
}
return apiServerOperator.Status.Conditions, nil
},
AssertDBPopulatedFunc: func(t testing.TB, errorStore map[string]int, statStore map[string]int) {
tokenCount, ok := statStore[tokenStatsKey]
if !ok {
err := errors.New("missing oauth access tokens count stats, can't continue the test")
require.NoError(t, err)
}
if tokenCount < 14000 {
err := fmt.Errorf("expected to create at least 14000 tokens but %d were created", tokenCount)
require.NoError(t, err)
}
t.Logf("Created %d access tokens", tokenCount)
},
AssertMigrationTime: func(t testing.TB, migrationTime time.Duration) {
t.Logf("migration took %v", migrationTime)
expectedMigrationTime := 10 * time.Minute
if migrationTime > expectedMigrationTime {
t.Errorf("migration took too long (%v), expected it to take no more than %v", migrationTime, expectedMigrationTime)
}
},
DBLoaderWorkers: 3,
DBLoaderFunc: library.DBLoaderRepeat(1, false,
library.DBLoaderRepeatParallel(5010, 50, false, createAccessTokenWrapper(ctx, clientSet.TokenClient), reportSecret)),
EncryptionProvider: library.EncryptionProvider{
APIServerEncryption: configv1.APIServerEncryption{Type: configv1.EncryptionType("aescbc")},
},
})
}

func createAccessTokenWrapper(ctx context.Context, tokenClient oauthclient.OAuthAccessTokensGetter) library.DBLoaderFuncType {
return func(_ kubernetes.Interface, namespace string, errorCollector func(error), statsCollector func(string)) error {
_, tokenNameHash := operatorlibrary.GenerateOAuthTokenPair()
token := &oauthapiv1.OAuthAccessToken{
ObjectMeta: metav1.ObjectMeta{
Name: tokenNameHash,
},
RefreshToken: "I have no special talents. I am only passionately curious",
UserName: "kube:admin",
Scopes: []string{"user:full"},
RedirectURI: "redirect.me.to.token.of.life",
ClientName: "console",
UserUID: "non-existing-user-id",
}
_, err := tokenClient.OAuthAccessTokens().Create(ctx, token, metav1.CreateOptions{})
return err
}
}

func reportSecret(_ kubernetes.Interface, _ string, _ func(error), statsCollector func(string)) error {
statsCollector(tokenStatsKey)
return nil
}

func getPerfClients(t *testing.T) operatorencryption.ClientSet {
t.Helper()

kubeConfig := operatorlibrary.NewClientConfigForTest(t)

kubeConfig.QPS = 300
kubeConfig.Burst = 600

return operatorencryption.GetClientsFor(t, kubeConfig)
testPerfEncryptionTypeAESCBC(tt)
}
71 changes: 6 additions & 65 deletions test/e2e-encryption-rotation/e2e-encryption-rotation_test.go
Original file line number Diff line number Diff line change
@@ -1,73 +1,14 @@
package e2e_encryption_rotation

import (
"context"
"encoding/json"
"fmt"
"testing"

configv1 "github.com/openshift/api/config/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"

oauthapiconfigobservercontroller "github.com/openshift/cluster-authentication-operator/pkg/operator/configobservation/configobservercontroller"
operatorencryption "github.com/openshift/cluster-authentication-operator/test/library/encryption"
library "github.com/openshift/library-go/test/library/encryption"
)

// TestEncryptionRotation first encrypts data with aescbc key
// then it forces a key rotation by setting the "encyrption.Reason" in the operator's configuration file
// 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-aws-operator-encryption-rotation-serial-ote job.
// Eventually all tests will be run only as part of the OTE framework.
func TestEncryptionRotation(t *testing.T) {
ctx := context.TODO()
library.TestEncryptionRotation(ctx, t, library.RotationScenario{
BasicScenario: library.BasicScenario{
Namespace: "openshift-config-managed",
LabelSelector: "encryption.apiserver.operator.openshift.io/component" + "=" + "openshift-oauth-apiserver",
EncryptionConfigSecretName: fmt.Sprintf("encryption-config-openshift-oauth-apiserver"),
EncryptionConfigSecretNamespace: "openshift-config-managed",
OperatorNamespace: "openshift-authentication-operator",
TargetGRs: operatorencryption.DefaultTargetGRs,
AssertFunc: operatorencryption.AssertTokens,
},
CreateResourceFunc: func(t testing.TB, _ library.ClientSet, _ string) runtime.Object {
return operatorencryption.CreateAndStoreTokenOfLife(ctx, t, operatorencryption.GetClients(t))
},
GetRawResourceFunc: func(t testing.TB, clientSet library.ClientSet, _ string) string {
return operatorencryption.GetRawTokenOfLife(t, clientSet)
},
EncryptionProvider: library.EncryptionProvider{
APIServerEncryption: configv1.APIServerEncryption{Type: configv1.EncryptionType("aescbc")},
},
ForceRotationFunc: library.StaticEncryptionForceRotation(func(rawUnsupportedEncryptionCfg []byte) error {
cs := operatorencryption.GetClients(t)
authOperator, err := cs.OperatorClient.Get(ctx, "cluster", metav1.GetOptions{})
if err != nil {
return err
}

unsupportedConfigAsMap := map[string]interface{}{}
if len(authOperator.Spec.UnsupportedConfigOverrides.Raw) > 0 {
if err := json.Unmarshal(authOperator.Spec.UnsupportedConfigOverrides.Raw, &unsupportedConfigAsMap); err != nil {
return err
}
}
unsupportedEncryptionConfigAsMap := map[string]interface{}{}
if err := json.Unmarshal(rawUnsupportedEncryptionCfg, &unsupportedEncryptionConfigAsMap); err != nil {
return err
}
if err := unstructured.SetNestedMap(unsupportedConfigAsMap, unsupportedEncryptionConfigAsMap, oauthapiconfigobservercontroller.OAuthAPIServerConfigPrefix); err != nil {
return err
}
rawUnsupportedCfg, err := json.Marshal(unsupportedConfigAsMap)
if err != nil {
return err
}
authOperator.Spec.UnsupportedConfigOverrides.Raw = rawUnsupportedCfg

_, err = cs.OperatorClient.Update(ctx, authOperator, metav1.UpdateOptions{})
return err
}),
WaitForRotationCompleteFunc: library.WaitForNextEncryptionKeyRotation(),
})
testEncryptionRotation(t)
}
Loading