From 40487d56ce8a04677d510e450ea5a5e21d85f3d3 Mon Sep 17 00:00:00 2001 From: Anatolii Bazko Date: Thu, 7 Oct 2021 15:49:33 +0300 Subject: [PATCH] feat: Enhancements in the way how OAuth2 providers can be configured (#1126) * feat: Enhancements in the way how OAuth2 providers can be configured Signed-off-by: Anatolii Bazko Co-authored-by: Serhii Leshchenko --- pkg/deploy/defaults.go | 14 +- pkg/deploy/server/server_configmap.go | 17 +- pkg/deploy/server/server_deployment.go | 151 +++++++++--- pkg/deploy/server/server_deployment_test.go | 249 ++++++++++++++++---- 4 files changed, 354 insertions(+), 77 deletions(-) diff --git a/pkg/deploy/defaults.go b/pkg/deploy/defaults.go index 2d4fdb876..19574df50 100644 --- a/pkg/deploy/defaults.go +++ b/pkg/deploy/defaults.go @@ -153,9 +153,17 @@ const ( DefaultPostgresCpuLimit = "500m" DefaultPostgresCpuRequest = "100m" - BitBucketOAuthConfigMountPath = "/che-conf/oauth/bitbucket" - BitBucketOAuthConfigPrivateKey = "private.key" - BitBucketOAuthConfigConsumerKey = "consumer.key" + BitBucketOAuthConfigMountPath = "/che-conf/oauth/bitbucket" + BitBucketOAuthConfigPrivateKeyFileName = "private.key" + BitBucketOAuthConfigConsumerKeyFileName = "consumer.key" + + GitHubOAuthConfigMountPath = "/che-conf/oauth/github" + GitHubOAuthConfigClientIdFileName = "id" + GitHubOAuthConfigClientSecretFileName = "secret" + + GitLabOAuthConfigMountPath = "/che-conf/oauth/gitlab" + GitLabOAuthConfigClientIdFileName = "id" + GitLabOAuthConfigClientSecretFileName = "secret" ) func InitDefaults(defaultsPath string) { diff --git a/pkg/deploy/server/server_configmap.go b/pkg/deploy/server/server_configmap.go index 7c820c185..5af09dde4 100644 --- a/pkg/deploy/server/server_configmap.go +++ b/pkg/deploy/server/server_configmap.go @@ -308,15 +308,24 @@ func (s *Server) getCheConfigMapData() (cheEnv map[string]string, err error) { addMap(cheEnv, s.deployContext.CheCluster.Spec.Server.CustomCheProperties) - // Update BitBucket endpoints - secrets, err := deploy.GetSecrets(s.deployContext, map[string]string{ + err = setBitbucketEndpoints(s.deployContext, cheEnv) + if err != nil { + return nil, err + } + + return cheEnv, nil +} + +func setBitbucketEndpoints(deployContext *deploy.DeployContext, cheEnv map[string]string) error { + secrets, err := deploy.GetSecrets(deployContext, map[string]string{ deploy.KubernetesPartOfLabelKey: deploy.CheEclipseOrg, deploy.KubernetesComponentLabelKey: deploy.OAuthScmConfiguration, }, map[string]string{ deploy.CheEclipseOrgOAuthScmServer: "bitbucket", }) + if err != nil { - return nil, err + return err } else if len(secrets) == 1 { serverEndpoint := secrets[0].Annotations[deploy.CheEclipseOrgScmServerEndpoint] endpoints, exists := cheEnv["CHE_INTEGRATION_BITBUCKET_SERVER__ENDPOINTS"] @@ -327,7 +336,7 @@ func (s *Server) getCheConfigMapData() (cheEnv map[string]string, err error) { } } - return cheEnv, nil + return nil } func GetCheConfigMapVersion(deployContext *deploy.DeployContext) string { diff --git a/pkg/deploy/server/server_deployment.go b/pkg/deploy/server/server_deployment.go index f97143875..9e93c7bc6 100644 --- a/pkg/deploy/server/server_deployment.go +++ b/pkg/deploy/server/server_deployment.go @@ -264,6 +264,16 @@ func (s Server) getDeploymentSpec() (*appsv1.Deployment, error) { return nil, err } + err = MountGitHubOAuthConfig(s.deployContext, deployment) + if err != nil { + return nil, err + } + + err = MountGitLabOAuthConfig(s.deployContext, deployment) + if err != nil { + return nil, err + } + container := &deployment.Spec.Template.Spec.Containers[0] chePostgresSecret := s.deployContext.CheCluster.Spec.Database.ChePostgresSecret if len(chePostgresSecret) > 0 { @@ -374,7 +384,6 @@ func GetFullCheServerImageLink(checluster *orgv1.CheCluster) string { } func MountBitBucketOAuthConfig(deployContext *deploy.DeployContext, deployment *appsv1.Deployment) error { - // mount BitBucket configuration secrets, err := deploy.GetSecrets(deployContext, map[string]string{ deploy.KubernetesPartOfLabelKey: deploy.CheEclipseOrg, deploy.KubernetesComponentLabelKey: deploy.OAuthScmConfiguration, @@ -387,36 +396,120 @@ func MountBitBucketOAuthConfig(deployContext *deploy.DeployContext, deployment * } else if len(secrets) > 1 { return errors.New("More than 1 BitBucket OAuth configuration secrets found") } else if len(secrets) == 1 { - // mount secrets - container := &deployment.Spec.Template.Spec.Containers[0] - deployment.Spec.Template.Spec.Volumes = append(deployment.Spec.Template.Spec.Volumes, - corev1.Volume{ - Name: secrets[0].Name, - VolumeSource: corev1.VolumeSource{ - Secret: &corev1.SecretVolumeSource{ - SecretName: secrets[0].Name, - }, - }, - }) - container.VolumeMounts = append(container.VolumeMounts, - corev1.VolumeMount{ - Name: secrets[0].Name, - MountPath: deploy.BitBucketOAuthConfigMountPath, - }) - - // mount env - endpoint := secrets[0].Annotations[deploy.CheEclipseOrgScmServerEndpoint] - container.Env = append(container.Env, corev1.EnvVar{ - Name: "CHE_OAUTH1_BITBUCKET_CONSUMERKEYPATH", - Value: deploy.BitBucketOAuthConfigMountPath + "/" + deploy.BitBucketOAuthConfigConsumerKey, - }, corev1.EnvVar{ - Name: "CHE_OAUTH1_BITBUCKET_PRIVATEKEYPATH", - Value: deploy.BitBucketOAuthConfigMountPath + "/" + deploy.BitBucketOAuthConfigPrivateKey, - }, corev1.EnvVar{ - Name: "CHE_OAUTH1_BITBUCKET_ENDPOINT", - Value: endpoint, + mountSecret(deployment, &secrets[0], deploy.BitBucketOAuthConfigMountPath) + mountEnv(deployment, []corev1.EnvVar{ + { + Name: "CHE_OAUTH1_BITBUCKET_CONSUMERKEYPATH", + Value: deploy.BitBucketOAuthConfigMountPath + "/" + deploy.BitBucketOAuthConfigConsumerKeyFileName, + }, { + Name: "CHE_OAUTH1_BITBUCKET_PRIVATEKEYPATH", + Value: deploy.BitBucketOAuthConfigMountPath + "/" + deploy.BitBucketOAuthConfigPrivateKeyFileName, + }, }) + + endpoint := secrets[0].Annotations[deploy.CheEclipseOrgScmServerEndpoint] + if endpoint != "" { + mountEnv(deployment, []corev1.EnvVar{{ + Name: "CHE_OAUTH1_BITBUCKET_ENDPOINT", + Value: endpoint, + }}) + } } return nil } + +func MountGitHubOAuthConfig(deployContext *deploy.DeployContext, deployment *appsv1.Deployment) error { + secrets, err := deploy.GetSecrets(deployContext, map[string]string{ + deploy.KubernetesPartOfLabelKey: deploy.CheEclipseOrg, + deploy.KubernetesComponentLabelKey: deploy.OAuthScmConfiguration, + }, map[string]string{ + deploy.CheEclipseOrgOAuthScmServer: "github", + }) + + if err != nil { + return err + } else if len(secrets) > 1 { + return errors.New("More than 1 GitHub OAuth configuration secrets found") + } else if len(secrets) == 1 { + mountSecret(deployment, &secrets[0], deploy.GitHubOAuthConfigMountPath) + mountEnv(deployment, []corev1.EnvVar{ + { + Name: "CHE_OAUTH2_GITHUB_CLIENTID__FILEPATH", + Value: deploy.GitHubOAuthConfigMountPath + "/" + deploy.GitHubOAuthConfigClientIdFileName, + }, { + Name: "CHE_OAUTH2_GITHUB_CLIENTSECRET__FILEPATH", + Value: deploy.GitHubOAuthConfigMountPath + "/" + deploy.GitHubOAuthConfigClientSecretFileName, + }, + }) + + endpoint := secrets[0].Annotations[deploy.CheEclipseOrgScmServerEndpoint] + if endpoint != "" { + mountEnv(deployment, []corev1.EnvVar{{ + Name: "CHE_INTEGRATION_GITHUB_SERVER__ENDPOINTS", + Value: endpoint, + }}) + } + } + + return nil +} + +func MountGitLabOAuthConfig(deployContext *deploy.DeployContext, deployment *appsv1.Deployment) error { + secrets, err := deploy.GetSecrets(deployContext, map[string]string{ + deploy.KubernetesPartOfLabelKey: deploy.CheEclipseOrg, + deploy.KubernetesComponentLabelKey: deploy.OAuthScmConfiguration, + }, map[string]string{ + deploy.CheEclipseOrgOAuthScmServer: "gitlab", + }) + + if err != nil { + return err + } else if len(secrets) > 1 { + return errors.New("More than 1 GitLab OAuth configuration secrets found") + } else if len(secrets) == 1 { + mountSecret(deployment, &secrets[0], deploy.GitLabOAuthConfigMountPath) + mountEnv(deployment, []corev1.EnvVar{ + { + Name: "CHE_OAUTH_GITLAB_CLIENTID__FILEPATH", + Value: deploy.GitLabOAuthConfigMountPath + "/" + deploy.GitLabOAuthConfigClientIdFileName, + }, { + Name: "CHE_OAUTH_GITLAB_CLIENTSECRET__FILEPATH", + Value: deploy.GitLabOAuthConfigMountPath + "/" + deploy.GitLabOAuthConfigClientSecretFileName, + }, + }) + + endpoint := secrets[0].Annotations[deploy.CheEclipseOrgScmServerEndpoint] + if endpoint != "" { + mountEnv(deployment, []corev1.EnvVar{{ + Name: "CHE_INTEGRATION_GITLAB_SERVER__ENDPOINTS", + Value: endpoint, + }}) + } + } + + return nil +} + +func mountSecret(deployment *appsv1.Deployment, secret *corev1.Secret, mountPath string) { + container := &deployment.Spec.Template.Spec.Containers[0] + deployment.Spec.Template.Spec.Volumes = append(deployment.Spec.Template.Spec.Volumes, + corev1.Volume{ + Name: secret.Name, + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: secret.Name, + }, + }, + }) + container.VolumeMounts = append(container.VolumeMounts, + corev1.VolumeMount{ + Name: secret.Name, + MountPath: mountPath, + }) +} + +func mountEnv(deployment *appsv1.Deployment, envVar []corev1.EnvVar) { + container := &deployment.Spec.Template.Spec.Containers[0] + container.Env = append(container.Env, envVar...) +} diff --git a/pkg/deploy/server/server_deployment_test.go b/pkg/deploy/server/server_deployment_test.go index f6d1e21c6..432a8d75e 100644 --- a/pkg/deploy/server/server_deployment_test.go +++ b/pkg/deploy/server/server_deployment_test.go @@ -13,10 +13,9 @@ package server import ( "os" - "reflect" "github.com/eclipse-che/che-operator/pkg/util" - "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/assert" "github.com/eclipse-che/che-operator/pkg/deploy" @@ -119,7 +118,6 @@ func TestMountBitBucketOAuthEnvVar(t *testing.T) { type testCase struct { name string initObjects []runtime.Object - cheCluster *orgv1.CheCluster expectedConsumerKeyPathEnv corev1.EnvVar expectedPrivateKeyPathEnv corev1.EnvVar expectedEndpointEnv corev1.EnvVar @@ -145,7 +143,7 @@ func TestMountBitBucketOAuthEnvVar(t *testing.T) { }, Annotations: map[string]string{ "che.eclipse.org/oauth-scm-server": "bitbucket", - "che.eclipse.org/scm-server-endpoint": "bitbucket_endpoint", + "che.eclipse.org/scm-server-endpoint": "endpoint", }, }, Data: map[string][]byte{ @@ -154,11 +152,6 @@ func TestMountBitBucketOAuthEnvVar(t *testing.T) { }, }, }, - cheCluster: &orgv1.CheCluster{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "eclipse-che", - }, - }, expectedConsumerKeyPathEnv: corev1.EnvVar{ Name: "CHE_OAUTH1_BITBUCKET_CONSUMERKEYPATH", Value: "/che-conf/oauth/bitbucket/consumer.key", @@ -169,7 +162,7 @@ func TestMountBitBucketOAuthEnvVar(t *testing.T) { }, expectedEndpointEnv: corev1.EnvVar{ Name: "CHE_OAUTH1_BITBUCKET_ENDPOINT", - Value: "bitbucket_endpoint", + Value: "endpoint", }, expectedVolume: corev1.Volume{ Name: "github-oauth-config", @@ -188,51 +181,225 @@ func TestMountBitBucketOAuthEnvVar(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { - logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true))) - orgv1.SchemeBuilder.AddToScheme(scheme.Scheme) - testCase.initObjects = append(testCase.initObjects) - cli := fake.NewFakeClientWithScheme(scheme.Scheme, testCase.initObjects...) - - deployContext := &deploy.DeployContext{ - CheCluster: testCase.cheCluster, - ClusterAPI: deploy.ClusterAPI{ - Client: cli, - Scheme: scheme.Scheme, - }, - Proxy: &deploy.Proxy{}, - } + deployContext := deploy.GetTestDeployContext(nil, testCase.initObjects) server := NewServer(deployContext) deployment, err := server.getDeploymentSpec() - if err != nil { - t.Fatalf("Error creating deployment: %v", err) - } + assert.Nil(t, err, "Unexpected error occurred %v", err) container := &deployment.Spec.Template.Spec.Containers[0] + env := util.FindEnv(container.Env, "CHE_OAUTH1_BITBUCKET_CONSUMERKEYPATH") - if !reflect.DeepEqual(testCase.expectedConsumerKeyPathEnv, *env) { - t.Errorf("Expected Env and Env returned from API server differ (-want, +got): %v", cmp.Diff(testCase.expectedConsumerKeyPathEnv, env)) - } + assert.NotNil(t, env) + assert.Equal(t, testCase.expectedConsumerKeyPathEnv, *env) env = util.FindEnv(container.Env, "CHE_OAUTH1_BITBUCKET_PRIVATEKEYPATH") - if !reflect.DeepEqual(testCase.expectedPrivateKeyPathEnv, *env) { - t.Errorf("Expected Env and Env returned from API server differ (-want, +got): %v", cmp.Diff(testCase.expectedPrivateKeyPathEnv, env)) - } + assert.NotNil(t, env) + assert.Equal(t, testCase.expectedPrivateKeyPathEnv, *env) env = util.FindEnv(container.Env, "CHE_OAUTH1_BITBUCKET_ENDPOINT") - if !reflect.DeepEqual(testCase.expectedEndpointEnv, *env) { - t.Errorf("Expected Env and Env returned from API server differ (-want, +got): %v", cmp.Diff(testCase.expectedEndpointEnv, env)) - } + assert.NotNil(t, env) + assert.Equal(t, testCase.expectedEndpointEnv, *env) volume := util.FindVolume(deployment.Spec.Template.Spec.Volumes, "github-oauth-config") - if !reflect.DeepEqual(testCase.expectedVolume, volume) { - t.Errorf("Expected Volume and Volume returned from API server differ (-want, +got): %v", cmp.Diff(testCase.expectedVolume, volume)) - } + assert.NotNil(t, volume) + assert.Equal(t, testCase.expectedVolume, volume) volumeMount := util.FindVolumeMount(container.VolumeMounts, "github-oauth-config") - if !reflect.DeepEqual(testCase.expectedVolumeMount, volumeMount) { - t.Errorf("Expected VolumeMount and VolumeMount returned from API server differ (-want, +got): %v", cmp.Diff(testCase.expectedVolumeMount, volumeMount)) - } + assert.NotNil(t, volumeMount) + assert.Equal(t, testCase.expectedVolumeMount, volumeMount) + }) + } +} + +func TestMountGitHubOAuthEnvVar(t *testing.T) { + type testCase struct { + name string + initObjects []runtime.Object + expectedIdKeyPathEnv corev1.EnvVar + expectedSecretKeyPathEnv corev1.EnvVar + expectedEndpointEnv corev1.EnvVar + expectedVolume corev1.Volume + expectedVolumeMount corev1.VolumeMount + } + + testCases := []testCase{ + { + name: "Test", + initObjects: []runtime.Object{ + &corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + Kind: "Secret", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "github-oauth-config", + Namespace: "eclipse-che", + Labels: map[string]string{ + "app.kubernetes.io/part-of": "che.eclipse.org", + "app.kubernetes.io/component": "oauth-scm-configuration", + }, + Annotations: map[string]string{ + "che.eclipse.org/oauth-scm-server": "github", + "che.eclipse.org/scm-server-endpoint": "endpoint", + }, + }, + Data: map[string][]byte{ + "id": []byte("some_id"), + "secret": []byte("some_secret"), + }, + }, + }, + expectedIdKeyPathEnv: corev1.EnvVar{ + Name: "CHE_OAUTH2_GITHUB_CLIENTID__FILEPATH", + Value: "/che-conf/oauth/github/id", + }, + expectedSecretKeyPathEnv: corev1.EnvVar{ + Name: "CHE_OAUTH2_GITHUB_CLIENTSECRET__FILEPATH", + Value: "/che-conf/oauth/github/secret", + }, + expectedEndpointEnv: corev1.EnvVar{ + Name: "CHE_INTEGRATION_GITHUB_SERVER__ENDPOINTS", + Value: "endpoint", + }, + expectedVolume: corev1.Volume{ + Name: "github-oauth-config", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "github-oauth-config", + }, + }, + }, + expectedVolumeMount: corev1.VolumeMount{ + Name: "github-oauth-config", + MountPath: "/che-conf/oauth/github", + }, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + deployContext := deploy.GetTestDeployContext(nil, testCase.initObjects) + + server := NewServer(deployContext) + deployment, err := server.getDeploymentSpec() + assert.Nil(t, err, "Unexpected error %v", err) + + container := &deployment.Spec.Template.Spec.Containers[0] + + env := util.FindEnv(container.Env, "CHE_OAUTH2_GITHUB_CLIENTID__FILEPATH") + assert.NotNil(t, env) + assert.Equal(t, testCase.expectedIdKeyPathEnv, *env) + + env = util.FindEnv(container.Env, "CHE_OAUTH2_GITHUB_CLIENTSECRET__FILEPATH") + assert.NotNil(t, env) + assert.Equal(t, testCase.expectedSecretKeyPathEnv, *env) + + volume := util.FindVolume(deployment.Spec.Template.Spec.Volumes, "github-oauth-config") + assert.NotNil(t, volume) + assert.Equal(t, testCase.expectedVolume, volume) + + volumeMount := util.FindVolumeMount(container.VolumeMounts, "github-oauth-config") + assert.NotNil(t, volumeMount) + assert.Equal(t, testCase.expectedVolumeMount, volumeMount) + }) + } +} + +func TestMountGitLabOAuthEnvVar(t *testing.T) { + type testCase struct { + name string + initObjects []runtime.Object + expectedIdKeyPathEnv corev1.EnvVar + expectedSecretKeyPathEnv corev1.EnvVar + expectedEndpointEnv corev1.EnvVar + expectedVolume corev1.Volume + expectedVolumeMount corev1.VolumeMount + } + + testCases := []testCase{ + { + name: "Test", + initObjects: []runtime.Object{ + &corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + Kind: "Secret", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "gitlab-oauth-config", + Namespace: "eclipse-che", + Labels: map[string]string{ + "app.kubernetes.io/part-of": "che.eclipse.org", + "app.kubernetes.io/component": "oauth-scm-configuration", + }, + Annotations: map[string]string{ + "che.eclipse.org/oauth-scm-server": "gitlab", + "che.eclipse.org/scm-server-endpoint": "endpoint", + }, + }, + Data: map[string][]byte{ + "id": []byte("some_id"), + "secret": []byte("some_secret"), + }, + }, + }, + expectedIdKeyPathEnv: corev1.EnvVar{ + Name: "CHE_OAUTH_GITLAB_CLIENTID__FILEPATH", + Value: "/che-conf/oauth/gitlab/id", + }, + expectedSecretKeyPathEnv: corev1.EnvVar{ + Name: "CHE_OAUTH_GITLAB_CLIENTSECRET__FILEPATH", + Value: "/che-conf/oauth/gitlab/secret", + }, + expectedEndpointEnv: corev1.EnvVar{ + Name: "CHE_INTEGRATION_GITLAB_SERVER__ENDPOINTS", + Value: "endpoint", + }, + expectedVolume: corev1.Volume{ + Name: "gitlab-oauth-config", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "gitlab-oauth-config", + }, + }, + }, + expectedVolumeMount: corev1.VolumeMount{ + Name: "gitlab-oauth-config", + MountPath: "/che-conf/oauth/gitlab", + }, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + deployContext := deploy.GetTestDeployContext(nil, testCase.initObjects) + + server := NewServer(deployContext) + deployment, err := server.getDeploymentSpec() + assert.Nil(t, err, "Unexpected error %v", err) + + container := &deployment.Spec.Template.Spec.Containers[0] + + env := util.FindEnv(container.Env, "CHE_OAUTH_GITLAB_CLIENTID__FILEPATH") + assert.NotNil(t, env) + assert.Equal(t, testCase.expectedIdKeyPathEnv, *env) + + env = util.FindEnv(container.Env, "CHE_OAUTH_GITLAB_CLIENTSECRET__FILEPATH") + assert.NotNil(t, env) + assert.Equal(t, testCase.expectedSecretKeyPathEnv, *env) + + env = util.FindEnv(container.Env, "CHE_INTEGRATION_GITLAB_SERVER__ENDPOINTS") + assert.NotNil(t, env) + assert.Equal(t, testCase.expectedEndpointEnv, *env) + + volume := util.FindVolume(deployment.Spec.Template.Spec.Volumes, "gitlab-oauth-config") + assert.NotNil(t, volume) + assert.Equal(t, testCase.expectedVolume, volume) + + volumeMount := util.FindVolumeMount(container.VolumeMounts, "gitlab-oauth-config") + assert.NotNil(t, volumeMount) + assert.Equal(t, testCase.expectedVolumeMount, volumeMount) }) } }