Allow to configure github and bitbucket oauth config (#677)

* Allow to configure github and bitbucket oauth config

Signed-off-by: Anatolii Bazko <abazko@redhat.com>
pull/679/head
Anatolii Bazko 2021-02-17 14:32:04 +02:00 committed by GitHub
parent 5f2b348932
commit 75da259f72
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 879 additions and 109 deletions

View File

@ -27,7 +27,7 @@ RUN export ARCH="$(uname -m)" && if [[ ${ARCH} == "x86_64" ]]; then export ARCH=
GOOS=linux GOARCH=${ARCH} CGO_ENABLED=0 go build -mod=vendor -o /tmp/che-operator/che-operator cmd/manager/main.go
# https://access.redhat.com/containers/?tab=tags#/registry.access.redhat.com/ubi8-minimal
FROM registry.access.redhat.com/ubi8-minimal:8.3-230
FROM registry.access.redhat.com/ubi8-minimal:8.3-291
COPY --from=builder /tmp/che-operator/che-operator /usr/local/bin/che-operator
COPY --from=builder /che-operator/templates/keycloak-provision.sh /tmp/keycloak-provision.sh

View File

@ -85,13 +85,13 @@ metadata:
categories: Developer Tools
certified: "false"
containerImage: quay.io/eclipse/che-operator:nightly
createdAt: "2021-02-15T16:16:52Z"
createdAt: "2021-02-17T11:12:17Z"
description: A Kube-native development solution that delivers portable and collaborative
developer workspaces.
operatorframework.io/suggested-namespace: eclipse-che
repository: https://github.com/eclipse/che-operator
support: Eclipse Foundation
name: eclipse-che-preview-kubernetes.v7.27.0-102.nightly
name: eclipse-che-preview-kubernetes.v7.27.0-104.nightly
namespace: placeholder
spec:
apiservicedefinitions: {}
@ -482,7 +482,7 @@ spec:
- name: RELATED_IMAGE_che_tls_secrets_creation_job
value: quay.io/eclipse/che-tls-secret-creator:alpine-d1ed4ad
- name: RELATED_IMAGE_pvc_jobs
value: registry.access.redhat.com/ubi8-minimal:8.3-230
value: registry.access.redhat.com/ubi8-minimal:8.3-291
- name: RELATED_IMAGE_postgres
value: quay.io/eclipse/che--centos--postgresql-96-centos7:9.6-b681d78125361519180a6ac05242c296f8906c11eab7e207b5ca9a89b6344392
- name: RELATED_IMAGE_keycloak
@ -685,4 +685,4 @@ spec:
maturity: stable
provider:
name: Eclipse Foundation
version: 7.27.0-102.nightly
version: 7.27.0-104.nightly

View File

@ -76,13 +76,13 @@ metadata:
categories: Developer Tools, OpenShift Optional
certified: "false"
containerImage: quay.io/eclipse/che-operator:nightly
createdAt: "2021-02-15T16:16:59Z"
createdAt: "2021-02-17T11:12:27Z"
description: A Kube-native development solution that delivers portable and collaborative
developer workspaces in OpenShift.
operatorframework.io/suggested-namespace: eclipse-che
repository: https://github.com/eclipse/che-operator
support: Eclipse Foundation
name: eclipse-che-preview-openshift.v7.27.0-102.nightly
name: eclipse-che-preview-openshift.v7.27.0-104.nightly
namespace: placeholder
spec:
apiservicedefinitions: {}
@ -548,7 +548,7 @@ spec:
- name: RELATED_IMAGE_devfile_registry
value: quay.io/eclipse/che-devfile-registry:nightly
- name: RELATED_IMAGE_pvc_jobs
value: registry.access.redhat.com/ubi8-minimal:8.3-230
value: registry.access.redhat.com/ubi8-minimal:8.3-291
- name: RELATED_IMAGE_postgres
value: quay.io/eclipse/che--centos--postgresql-96-centos7:9.6-b681d78125361519180a6ac05242c296f8906c11eab7e207b5ca9a89b6344392
- name: RELATED_IMAGE_keycloak
@ -757,4 +757,4 @@ spec:
maturity: stable
provider:
name: Eclipse Foundation
version: 7.27.0-102.nightly
version: 7.27.0-104.nightly

View File

@ -58,7 +58,7 @@ spec:
- name: RELATED_IMAGE_che_tls_secrets_creation_job
value: quay.io/eclipse/che-tls-secret-creator:alpine-d1ed4ad
- name: RELATED_IMAGE_pvc_jobs
value: registry.access.redhat.com/ubi8-minimal:8.3-230
value: registry.access.redhat.com/ubi8-minimal:8.3-291
- name: RELATED_IMAGE_postgres
value: quay.io/eclipse/che--centos--postgresql-96-centos7:9.6-b681d78125361519180a6ac05242c296f8906c11eab7e207b5ca9a89b6344392
- name: RELATED_IMAGE_keycloak

1
go.sum
View File

@ -1014,6 +1014,7 @@ google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191028173616-919d9bdd9fe6 h1:UXl+Zk3jqqcbEVV7ace5lrt4YdA4tXiz3f/KbmD29Vo=
google.golang.org/genproto v0.0.0-20191028173616-919d9bdd9fe6/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=

View File

@ -101,7 +101,8 @@ const (
KubernetesInstanceLabelKey = "app.kubernetes.io/instance"
KubernetesNameLabelKey = "app.kubernetes.io/name"
CheEclipseOrg = "che.eclipse.org"
CheEclipseOrg = "che.eclipse.org"
OAuthScmConfiguration = "oauth-scm-configuration"
// che.eclipse.org annotations
CheEclipseOrgMountPath = "che.eclipse.org/mount-path"
@ -109,6 +110,8 @@ const (
CheEclipseOrgEnvName = "che.eclipse.org/env-name"
CheEclipseOrgNamespace = "che.eclipse.org/namespace"
CheEclipseOrgGithubOAuthCredentials = "che.eclipse.org/github-oauth-credentials"
CheEclipseOrgOAuthScmServer = "che.eclipse.org/oauth-scm-server"
CheEclipseOrgScmServerEndpoint = "che.eclipse.org/scm-server-endpoint"
// components
IdentityProviderName = "keycloak"
@ -141,6 +144,10 @@ const (
DefaultPostgresMemoryRequest = "512Mi"
DefaultPostgresCpuLimit = "500m"
DefaultPostgresCpuRequest = "100m"
BitBucketOAuthConfigMountPath = "/che-conf/oauth/bitbucket"
BitBucketOAuthConfigPrivateKey = "private.key"
BitBucketOAuthConfigConsumerKey = "consumer.key"
)
func InitDefaults(defaultsPath string) {

View File

@ -137,6 +137,7 @@ func MountSecrets(specDeployment *appsv1.Deployment, deployContext *DeployContex
return strings.Compare(secrets.Items[i].Name, secrets.Items[j].Name) < 0
})
container := &specDeployment.Spec.Template.Spec.Containers[0]
for _, secretObj := range secrets.Items {
switch secretObj.Annotations[CheEclipseOrgMountAs] {
case "file":
@ -157,7 +158,7 @@ func MountSecrets(specDeployment *appsv1.Deployment, deployContext *DeployContex
}
specDeployment.Spec.Template.Spec.Volumes = append(specDeployment.Spec.Template.Spec.Volumes, volume)
specDeployment.Spec.Template.Spec.Containers[0].VolumeMounts = append(specDeployment.Spec.Template.Spec.Containers[0].VolumeMounts, volumeMount)
container.VolumeMounts = append(container.VolumeMounts, volumeMount)
case "env":
secret, err := GetSecret(deployContext, secretObj.Name, deployContext.CheCluster.Namespace)
@ -203,7 +204,7 @@ func MountSecrets(specDeployment *appsv1.Deployment, deployContext *DeployContex
},
},
}
specDeployment.Spec.Template.Spec.Containers[0].Env = append(specDeployment.Spec.Template.Spec.Containers[0].Env, env)
container.Env = append(container.Env, env)
}
}
}

View File

@ -89,12 +89,15 @@ func GetSpecKeycloakDeployment(
}
if clusterDeployment != nil {
env := clusterDeployment.Spec.Template.Spec.Containers[0].Env
for _, e := range env {
// To be compatible with prev deployments when "TRUSTPASS" env was used
if "TRUSTPASS" == e.Name || "SSO_TRUSTSTORE_PASSWORD" == e.Name {
trustpass = e.Value
break
// To be compatible with prev deployments when "TRUSTPASS" env was used
clusterContainer := &clusterDeployment.Spec.Template.Spec.Containers[0]
env := util.FindEnv(clusterContainer.Env, "TRUSTPASS")
if env != nil {
trustpass = env.Value
} else {
env := util.FindEnv(clusterContainer.Env, "SSO_TRUSTSTORE_PASSWORD")
if env != nil {
trustpass = env.Value
}
}
}
@ -477,6 +480,40 @@ func GetSpecKeycloakDeployment(
}
}
// Mount GITHUB_CLIENT_ID and GITHUB_SECRET to keycloak container
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 nil, err
} else if len(secrets) == 1 {
keycloakEnv = append(keycloakEnv, corev1.EnvVar{
Name: "GITHUB_CLIENT_ID",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
Key: "id",
LocalObjectReference: corev1.LocalObjectReference{
Name: secrets[0].Name,
},
},
},
}, corev1.EnvVar{
Name: "GITHUB_SECRET",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
Key: "secret",
LocalObjectReference: corev1.LocalObjectReference{
Name: secrets[0].Name,
},
},
},
})
}
for _, envvar := range proxyEnvVars {
keycloakEnv = append(keycloakEnv, envvar)
}

View File

@ -13,12 +13,15 @@ package identity_provider
import (
"os"
"reflect"
"github.com/eclipse/che-operator/pkg/util"
"github.com/google/go-cmp/cmp"
"github.com/eclipse/che-operator/pkg/deploy"
orgv1 "github.com/eclipse/che-operator/pkg/apis/org/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/scheme"
@ -117,3 +120,103 @@ func TestDeployment(t *testing.T) {
})
}
}
func TestMountGitHubOAuthEnvVar(t *testing.T) {
type testCase struct {
name string
initObjects []runtime.Object
cheCluster *orgv1.CheCluster
expectedIdEnv corev1.EnvVar
expectedSecretEnv corev1.EnvVar
}
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",
},
},
Data: map[string][]byte{
"id": []byte("some__id"),
"secret": []byte("some_secret"),
},
},
},
cheCluster: &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
},
},
expectedIdEnv: corev1.EnvVar{
Name: "GITHUB_CLIENT_ID",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
Key: "id",
LocalObjectReference: corev1.LocalObjectReference{
Name: "github-oauth-config",
},
},
},
},
expectedSecretEnv: corev1.EnvVar{
Name: "GITHUB_SECRET",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
Key: "secret",
LocalObjectReference: corev1.LocalObjectReference{
Name: "github-oauth-config",
},
},
},
},
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
logf.SetLogger(zap.LoggerTo(os.Stdout, 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{},
}
deployment, err := GetSpecKeycloakDeployment(deployContext, nil)
if err != nil {
t.Fatalf("Error creating deployment: %v", err)
}
container := &deployment.Spec.Template.Spec.Containers[0]
idEnv := util.FindEnv(container.Env, "GITHUB_CLIENT_ID")
if !reflect.DeepEqual(testCase.expectedIdEnv, *idEnv) {
t.Errorf("Expected Env and Env returned from API server differ (-want, +got): %v", cmp.Diff(testCase.expectedIdEnv, idEnv))
}
secretEnv := util.FindEnv(container.Env, "GITHUB_SECRET")
if !reflect.DeepEqual(testCase.expectedSecretEnv, *secretEnv) {
t.Errorf("Expected CR and CR returned from API server differ (-want, +got): %v", cmp.Diff(testCase.expectedSecretEnv, secretEnv))
}
})
}
}

View File

@ -13,22 +13,18 @@ package identity_provider
import (
"context"
"errors"
"fmt"
"strings"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/selection"
orgv1 "github.com/eclipse/che-operator/pkg/apis/org/v1"
"github.com/eclipse/che-operator/pkg/deploy"
"github.com/eclipse/che-operator/pkg/deploy/expose"
"github.com/eclipse/che-operator/pkg/util"
"github.com/google/go-cmp/cmp/cmpopts"
oauth "github.com/openshift/api/oauth/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
)
var (
@ -128,7 +124,7 @@ func syncKeycloakResources(deployContext *deploy.DeployContext) (bool, error) {
for {
cr.Status.KeycloakProvisoned = true
if err := deploy.UpdateCheCRStatus(deployContext, "status: provisioned with Keycloak", "true"); err != nil &&
errors.IsConflict(err) {
apierrors.IsConflict(err) {
reload(deployContext)
continue
@ -197,7 +193,7 @@ func SyncOpenShiftIdentityProviderItems(deployContext *deploy.DeployContext) (bo
for {
cr.Status.OpenShiftoAuthProvisioned = true
if err := deploy.UpdateCheCRStatus(deployContext, "status: provisioned with OpenShift identity provider", "true"); err != nil &&
errors.IsConflict(err) {
apierrors.IsConflict(err) {
reload(deployContext)
continue
@ -209,34 +205,37 @@ func SyncOpenShiftIdentityProviderItems(deployContext *deploy.DeployContext) (bo
return true, nil
}
// SyncGitHubOAuth provisions GitHub OAuth if secret with
// annotation `che.eclipse.org/github-oauth-credentials=true` is mounted into a container
// SyncGitHubOAuth provisions GitHub OAuth if secret with annotation
// `che.eclipse.org/github-oauth-credentials=true` or `che.eclipse.org/oauth-scm-configuration=github`
// is mounted into a container
func SyncGitHubOAuth(deployContext *deploy.DeployContext) (bool, error) {
cr := deployContext.CheCluster
// find mounted GitHug OAuth credentials
secrets := &corev1.SecretList{}
kubernetesPartOfLabelSelectorRequirement, _ := labels.NewRequirement(deploy.KubernetesPartOfLabelKey, selection.Equals, []string{deploy.CheEclipseOrg})
kubernetesComponentLabelSelectorRequirement, _ := labels.NewRequirement(deploy.KubernetesComponentLabelKey, selection.Equals, []string{deploy.IdentityProviderName + "-secret"})
listOptions := &client.ListOptions{
LabelSelector: labels.NewSelector().
Add(*kubernetesPartOfLabelSelectorRequirement).
Add(*kubernetesComponentLabelSelectorRequirement),
}
if err := deployContext.ClusterAPI.Client.List(context.TODO(), secrets, listOptions); err != nil {
// get legacy secret
legacySecrets, err := deploy.GetSecrets(deployContext, map[string]string{
deploy.KubernetesPartOfLabelKey: deploy.CheEclipseOrg,
deploy.KubernetesComponentLabelKey: deploy.IdentityProviderName + "-secret",
}, map[string]string{
deploy.CheEclipseOrgGithubOAuthCredentials: "true",
})
if err != nil {
return false, err
}
isGitHubOAuthCredentialsExists := false
for _, secret := range secrets.Items {
if secret.Annotations[deploy.CheEclipseOrgGithubOAuthCredentials] == "true" {
isGitHubOAuthCredentialsExists = true
break
}
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 false, err
} else if len(secrets)+len(legacySecrets) > 1 {
return false, errors.New("More than 1 GitHub OAuth configuration secrets found")
}
isGitHubOAuthCredentialsExists := len(secrets) == 1 || len(legacySecrets) == 1
cr := deployContext.CheCluster
if isGitHubOAuthCredentialsExists {
if !cr.Status.GitHubOAuthProvisioned {
if !util.IsTestMode() {

View File

@ -41,7 +41,7 @@ func TestSyncGitHubOAuth(t *testing.T) {
testCases := []testCase{
{
name: "Should provision GitHub OAuth",
name: "Should provision GitHub OAuth with legacy secret",
initCR: &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
@ -79,6 +79,42 @@ func TestSyncGitHubOAuth(t *testing.T) {
},
},
},
{
name: "Should provision GitHub OAuth",
initCR: &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
},
},
expectedCR: &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
ResourceVersion: "1",
},
Status: orgv1.CheClusterStatus{
GitHubOAuthProvisioned: true,
},
},
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",
},
},
},
},
},
{
name: "Should not provision GitHub OAuth",
initCR: &orgv1.CheCluster{

View File

@ -52,12 +52,10 @@ func GetSpecPostgresDeployment(deployContext *deploy.DeployContext, clusterDeplo
pullPolicy := corev1.PullPolicy(util.GetValue(string(deployContext.CheCluster.Spec.Database.PostgresImagePullPolicy), deploy.DefaultPullPolicyFromDockerImage(postgresImage)))
if clusterDeployment != nil {
env := clusterDeployment.Spec.Template.Spec.Containers[0].Env
for _, e := range env {
if "POSTGRESQL_ADMIN_PASSWORD" == e.Name {
postgresAdminPassword = e.Value
break
}
clusterContainer := &clusterDeployment.Spec.Template.Spec.Containers[0]
env := util.FindEnv(clusterContainer.Env, "POSTGRESQL_ADMIN_PASSWORD")
if env != nil {
postgresAdminPassword = env.Value
}
}
@ -179,9 +177,11 @@ func GetSpecPostgresDeployment(deployContext *deploy.DeployContext, clusterDeplo
},
}
container := &deployment.Spec.Template.Spec.Containers[0]
chePostgresSecret := deployContext.CheCluster.Spec.Database.ChePostgresSecret
if len(chePostgresSecret) > 0 {
deployment.Spec.Template.Spec.Containers[0].Env = append(deployment.Spec.Template.Spec.Containers[0].Env,
container.Env = append(container.Env,
corev1.EnvVar{
Name: "POSTGRESQL_USER",
ValueFrom: &corev1.EnvVarSource{
@ -204,7 +204,7 @@ func GetSpecPostgresDeployment(deployContext *deploy.DeployContext, clusterDeplo
},
})
} else {
deployment.Spec.Template.Spec.Containers[0].Env = append(deployment.Spec.Template.Spec.Containers[0].Env,
container.Env = append(container.Env,
corev1.EnvVar{
Name: "POSTGRESQL_USER",
Value: deployContext.CheCluster.Spec.Database.ChePostgresUser,

View File

@ -24,6 +24,8 @@ import (
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
k8slabels "k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/selection"
"k8s.io/apimachinery/pkg/types"
)
@ -97,6 +99,46 @@ func GetSecret(deployContext *DeployContext, name string, namespace string) (*co
return secret, nil
}
// Get all secrets by labels and annotations
func GetSecrets(deployContext *DeployContext, labels map[string]string, annotations map[string]string) ([]corev1.Secret, error) {
secrets := []corev1.Secret{}
labelSelector := k8slabels.NewSelector()
for k, v := range labels {
req, err := k8slabels.NewRequirement(k, selection.Equals, []string{v})
if err != nil {
return secrets, err
}
labelSelector = labelSelector.Add(*req)
}
listOptions := &client.ListOptions{
Namespace: deployContext.CheCluster.Namespace,
LabelSelector: labelSelector,
}
secretList := &corev1.SecretList{}
if err := deployContext.ClusterAPI.Client.List(context.TODO(), secretList, listOptions); err != nil {
return secrets, err
}
for _, secret := range secretList.Items {
annotationsOk := true
for k, v := range annotations {
_, annotationExists := secret.Annotations[k]
if !annotationExists || secret.Annotations[k] != v {
annotationsOk = false
break
}
}
if annotationsOk {
secrets = append(secrets, secret)
}
}
return secrets, nil
}
// GetSpecSecret return default secret config for given data
func GetSpecSecret(deployContext *DeployContext, name string, namespace string, data map[string][]byte) (*corev1.Secret, error) {
labels := GetLabels(deployContext.CheCluster, DefaultCheFlavor(deployContext.CheCluster))

165
pkg/deploy/sercet_test.go Normal file
View File

@ -0,0 +1,165 @@
//
// Copyright (c) 2012-2019 Red Hat, Inc.
// This program and the accompanying materials are made
// available under the terms of the Eclipse Public License 2.0
// which is available at https://www.eclipse.org/legal/epl-2.0/
//
// SPDX-License-Identifier: EPL-2.0
//
// Contributors:
// Red Hat, Inc. - initial API and implementation
//
package deploy
import (
"os"
orgv1 "github.com/eclipse/che-operator/pkg/apis/org/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/scheme"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
"testing"
)
func TestGetSecrets(t *testing.T) {
type testCase struct {
name string
labels map[string]string
annotations map[string]string
initObjects []runtime.Object
expectedAmount int
}
runtimeSecrets := []runtime.Object{
&corev1.Secret{
TypeMeta: metav1.TypeMeta{
Kind: "Secret",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "test2",
Namespace: "eclipse-che",
Labels: map[string]string{
"l1": "v1",
"l2": "v2",
},
Annotations: map[string]string{
"a1": "v1",
"a2": "v2",
},
},
},
&corev1.Secret{
TypeMeta: metav1.TypeMeta{
Kind: "Secret",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "test1",
Namespace: "eclipse-che",
Labels: map[string]string{
"l1": "v1",
"l3": "v3",
},
Annotations: map[string]string{
"a1": "v1",
"a3": "v3",
},
},
},
}
testCases := []testCase{
{
name: "Get secrets",
initObjects: []runtime.Object{},
labels: map[string]string{
"l1": "v1",
},
annotations: map[string]string{
"a1": "v1",
},
expectedAmount: 2,
},
{
name: "Get secrets",
initObjects: []runtime.Object{},
labels: map[string]string{
"l1": "v1",
},
annotations: map[string]string{
"a1": "v1",
"a2": "v2",
},
expectedAmount: 1,
},
{
name: "Get secrets",
initObjects: []runtime.Object{},
labels: map[string]string{
"l1": "v1",
"l2": "v2",
},
annotations: map[string]string{
"a1": "v1",
},
expectedAmount: 1,
},
{
name: "Get secrets, unknown label",
initObjects: []runtime.Object{},
labels: map[string]string{
"l4": "v4",
},
annotations: map[string]string{},
expectedAmount: 0,
},
{
name: "Get secrets, unknown annotation",
initObjects: []runtime.Object{},
labels: map[string]string{
"l1": "v1",
},
annotations: map[string]string{
"a4": "v4",
},
expectedAmount: 0,
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
logf.SetLogger(zap.LoggerTo(os.Stdout, true))
orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
testCase.initObjects = append(testCase.initObjects, runtimeSecrets...)
cli := fake.NewFakeClientWithScheme(scheme.Scheme, testCase.initObjects...)
deployContext := &DeployContext{
CheCluster: &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
},
},
ClusterAPI: ClusterAPI{
Client: cli,
NonCachedClient: cli,
Scheme: scheme.Scheme,
},
}
secrets, err := GetSecrets(deployContext, testCase.labels, testCase.annotations)
if err != nil {
t.Fatalf("Error getting secrets: %v", err)
}
if len(secrets) != testCase.expectedAmount {
t.Fatalf("Expected %d but found: %d", testCase.expectedAmount, len(secrets))
}
})
}
}

View File

@ -304,6 +304,26 @@ func GetCheConfigMapData(deployContext *deploy.DeployContext) (cheEnv map[string
}
addMap(cheEnv, deployContext.CheCluster.Spec.Server.CustomCheProperties)
// Update BitBucket endpoints
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
} else if len(secrets) == 1 {
serverEndpoint := secrets[0].Annotations[deploy.CheEclipseOrgScmServerEndpoint]
endpoints, exists := cheEnv["CHE_INTEGRATION_BITBUCKET_SERVER__ENDPOINTS"]
if exists {
cheEnv["CHE_INTEGRATION_BITBUCKET_SERVER__ENDPOINTS"] = endpoints + "," + serverEndpoint
} else {
cheEnv["CHE_INTEGRATION_BITBUCKET_SERVER__ENDPOINTS"] = serverEndpoint
}
}
return cheEnv, nil
}

View File

@ -13,7 +13,6 @@ package server
import (
"os"
"strings"
"testing"
"github.com/eclipse/che-operator/pkg/deploy"
@ -30,53 +29,74 @@ import (
)
func TestNewCheConfigMap(t *testing.T) {
type testCase struct {
name string
isOpenShift bool
isOpenShift4 bool
initObjects []runtime.Object
cheCluster *orgv1.CheCluster
expectedData map[string]string
}
// since all values are retrieved from CR or auto-generated
// some of them are explicitly set for this test to avoid using fake kube
// and creating a CR with all spec fields pre-populated
cr := &orgv1.CheCluster{}
cr.Spec.Server.CheHost = "myhostname.com"
cr.Spec.Server.TlsSupport = true
cr.Spec.Auth.OpenShiftoAuth = util.NewBoolPointer(true)
deployContext := &deploy.DeployContext{
CheCluster: cr,
Proxy: &deploy.Proxy{},
ClusterAPI: deploy.ClusterAPI{},
testCases := []testCase{
{
name: "Test",
initObjects: []runtime.Object{},
isOpenShift: true,
isOpenShift4: true,
cheCluster: &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
},
Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{
CheHost: "myhostname.com",
TlsSupport: true,
CustomCheProperties: map[string]string{
"CHE_WORKSPACE_NO_PROXY": "myproxy.myhostname.com",
},
},
Auth: orgv1.CheClusterSpecAuth{
OpenShiftoAuth: util.NewBoolPointer(true),
},
},
},
expectedData: map[string]string{
"CHE_INFRA_OPENSHIFT_OAUTH__IDENTITY__PROVIDER": "openshift-v4",
"CHE_API": "https://myhostname.com/api",
"CHE_WORKSPACE_NO_PROXY": "myproxy.myhostname.com",
},
},
}
expectedIdentityProvider := "openshift-v4"
util.IsOpenShift = true
util.IsOpenShift4 = true
cheEnv, _ := GetCheConfigMapData(deployContext)
testCm, _ := deploy.GetSpecConfigMap(deployContext, CheConfigMapName, cheEnv, CheConfigMapName)
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
logf.SetLogger(zap.LoggerTo(os.Stdout, true))
orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
testCase.initObjects = append(testCase.initObjects)
cli := fake.NewFakeClientWithScheme(scheme.Scheme, testCase.initObjects...)
nonCachedClient := fake.NewFakeClientWithScheme(scheme.Scheme, testCase.initObjects...)
identityProvider := testCm.Data["CHE_INFRA_OPENSHIFT_OAUTH__IDENTITY__PROVIDER"]
protocol := strings.Split(testCm.Data["CHE_API"], "://")[0]
if identityProvider != expectedIdentityProvider {
t.Errorf("Test failed. Expecting identity provider to be '%s' while got '%s'", expectedIdentityProvider, identityProvider)
}
if protocol != "https" {
t.Errorf("Test failed. Expecting 'https' protocol, got '%s'", protocol)
}
}
deployContext := &deploy.DeployContext{
CheCluster: testCase.cheCluster,
ClusterAPI: deploy.ClusterAPI{
Client: cli,
NonCachedClient: nonCachedClient,
Scheme: scheme.Scheme,
},
Proxy: &deploy.Proxy{},
}
func TestConfigMapOverride(t *testing.T) {
cr := &orgv1.CheCluster{}
cr.Spec.Server.CheHost = "myhostname.com"
cr.Spec.Server.TlsSupport = true
cr.Spec.Server.CustomCheProperties = map[string]string{
"CHE_WORKSPACE_NO_PROXY": "myproxy.myhostname.com",
}
cr.Spec.Auth.OpenShiftoAuth = util.NewBoolPointer(true)
deployContext := &deploy.DeployContext{
CheCluster: cr,
Proxy: &deploy.Proxy{},
ClusterAPI: deploy.ClusterAPI{},
}
cheEnv, _ := GetCheConfigMapData(deployContext)
testCm, _ := deploy.GetSpecConfigMap(deployContext, CheConfigMapName, cheEnv, CheConfigMapName)
if testCm.Data["CHE_WORKSPACE_NO_PROXY"] != "myproxy.myhostname.com" {
t.Errorf("Test failed. Expected myproxy.myhostname.com but was %s", testCm.Data["CHE_WORKSPACE_NO_PROXY"])
util.IsOpenShift = testCase.isOpenShift
util.IsOpenShift4 = testCase.isOpenShift4
actualData, err := GetCheConfigMapData(deployContext)
if err != nil {
t.Fatalf("Error creating ConfigMap data: %v", err)
}
util.ValidateContainData(actualData, testCase.expectedData, t)
})
}
}
@ -177,3 +197,130 @@ func TestConfigMap(t *testing.T) {
})
}
}
func TestUpdateBitBucketEndpoints(t *testing.T) {
type testCase struct {
name string
initObjects []runtime.Object
cheCluster *orgv1.CheCluster
expectedData map[string]string
}
testCases := []testCase{
{
name: "Test set BitBucket endpoints from secret",
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": "bitbucket",
"che.eclipse.org/scm-server-endpoint": "bitbucket_endpoint_2",
},
},
},
},
cheCluster: &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
},
},
expectedData: map[string]string{
"CHE_INTEGRATION_BITBUCKET_SERVER__ENDPOINTS": "bitbucket_endpoint_2",
},
},
{
name: "Test update BitBucket endpoints",
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": "bitbucket",
"che.eclipse.org/scm-server-endpoint": "bitbucket_endpoint_2",
},
},
},
},
cheCluster: &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
},
Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{
CustomCheProperties: map[string]string{
"CHE_INTEGRATION_BITBUCKET_SERVER__ENDPOINTS": "bitbucket_endpoint_1",
},
},
},
},
expectedData: map[string]string{
"CHE_INTEGRATION_BITBUCKET_SERVER__ENDPOINTS": "bitbucket_endpoint_1,bitbucket_endpoint_2",
},
},
{
name: "Test don't update BitBucket endpoints",
initObjects: []runtime.Object{},
cheCluster: &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
},
Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{
CustomCheProperties: map[string]string{
"CHE_INTEGRATION_BITBUCKET_SERVER__ENDPOINTS": "bitbucket_endpoint_1",
},
},
},
},
expectedData: map[string]string{
"CHE_INTEGRATION_BITBUCKET_SERVER__ENDPOINTS": "bitbucket_endpoint_1",
},
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
logf.SetLogger(zap.LoggerTo(os.Stdout, true))
orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
testCase.initObjects = append(testCase.initObjects)
cli := fake.NewFakeClientWithScheme(scheme.Scheme, testCase.initObjects...)
nonCachedClient := fake.NewFakeClientWithScheme(scheme.Scheme, testCase.initObjects...)
deployContext := &deploy.DeployContext{
CheCluster: testCase.cheCluster,
ClusterAPI: deploy.ClusterAPI{
Client: cli,
NonCachedClient: nonCachedClient,
Scheme: scheme.Scheme,
},
Proxy: &deploy.Proxy{},
}
actualData, err := GetCheConfigMapData(deployContext)
if err != nil {
t.Fatalf("Error creating ConfigMap data: %v", err)
}
util.ValidateContainData(actualData, testCase.expectedData, t)
})
}
}

View File

@ -12,6 +12,7 @@
package server
import (
"errors"
"strconv"
"strings"
@ -273,11 +274,17 @@ func GetSpecCheDeployment(deployContext *deploy.DeployContext) (*appsv1.Deployme
},
}
err = MountBitBucketOAuthConfig(deployContext, deployment)
if err != nil {
return nil, err
}
container := &deployment.Spec.Template.Spec.Containers[0]
cheMultiUser := deploy.GetCheMultiUser(deployContext.CheCluster)
if cheMultiUser == "true" {
chePostgresSecret := deployContext.CheCluster.Spec.Database.ChePostgresSecret
if len(chePostgresSecret) > 0 {
deployment.Spec.Template.Spec.Containers[0].Env = append(deployment.Spec.Template.Spec.Containers[0].Env,
container.Env = append(container.Env,
corev1.EnvVar{
Name: "CHE_JDBC_USERNAME",
ValueFrom: &corev1.EnvVarSource{
@ -311,7 +318,7 @@ func GetSpecCheDeployment(deployContext *deploy.DeployContext) (*appsv1.Deployme
},
},
}}
deployment.Spec.Template.Spec.Containers[0].VolumeMounts = []corev1.VolumeMount{
container.VolumeMounts = []corev1.VolumeMount{
{
MountPath: deploy.DefaultCheVolumeMountPath,
Name: deploy.DefaultCheVolumeClaimName,
@ -321,7 +328,7 @@ func GetSpecCheDeployment(deployContext *deploy.DeployContext) (*appsv1.Deployme
// configure probes if debug isn't set
cheDebug := util.GetValue(deployContext.CheCluster.Spec.Server.CheDebug, deploy.DefaultCheDebug)
if cheDebug != "true" {
deployment.Spec.Template.Spec.Containers[0].ReadinessProbe = &corev1.Probe{
container.ReadinessProbe = &corev1.Probe{
Handler: corev1.Handler{
HTTPGet: &corev1.HTTPGetAction{
Path: "/api/system/state",
@ -340,7 +347,7 @@ func GetSpecCheDeployment(deployContext *deploy.DeployContext) (*appsv1.Deployme
PeriodSeconds: 10,
SuccessThreshold: 1,
}
deployment.Spec.Template.Spec.Containers[0].LivenessProbe = &corev1.Probe{
container.LivenessProbe = &corev1.Probe{
Handler: corev1.Handler{
HTTPGet: &corev1.HTTPGetAction{
Path: "/api/system/state",
@ -406,3 +413,51 @@ func GetFullCheServerImageLink(checluster *orgv1.CheCluster) string {
imageParts := strings.Split(defaultCheServerImage, separator)
return imageParts[0] + ":" + checluster.Spec.Server.CheImageTag
}
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,
}, map[string]string{
deploy.CheEclipseOrgOAuthScmServer: "bitbucket",
})
if err != nil {
return err
} 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,
})
}
return nil
}

View File

@ -13,12 +13,15 @@ package server
import (
"os"
"reflect"
"github.com/eclipse/che-operator/pkg/util"
"github.com/google/go-cmp/cmp"
"github.com/eclipse/che-operator/pkg/deploy"
orgv1 "github.com/eclipse/che-operator/pkg/apis/org/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/scheme"
@ -110,3 +113,124 @@ func TestDeployment(t *testing.T) {
})
}
}
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
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": "bitbucket",
"che.eclipse.org/scm-server-endpoint": "bitbucket_endpoint",
},
},
Data: map[string][]byte{
"private.key": []byte("private_key"),
"consumer.key": []byte("consumer_key"),
},
},
},
cheCluster: &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
},
},
expectedConsumerKeyPathEnv: corev1.EnvVar{
Name: "CHE_OAUTH1_BITBUCKET_CONSUMERKEYPATH",
Value: "/che-conf/oauth/bitbucket/consumer.key",
},
expectedPrivateKeyPathEnv: corev1.EnvVar{
Name: "CHE_OAUTH1_BITBUCKET_PRIVATEKEYPATH",
Value: "/che-conf/oauth/bitbucket/private.key",
},
expectedEndpointEnv: corev1.EnvVar{
Name: "CHE_OAUTH1_BITBUCKET_ENDPOINT",
Value: "bitbucket_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/bitbucket",
},
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
logf.SetLogger(zap.LoggerTo(os.Stdout, 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{},
}
deployment, err := GetSpecCheDeployment(deployContext)
if err != nil {
t.Fatalf("Error creating deployment: %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))
}
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))
}
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))
}
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))
}
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))
}
})
}
}

View File

@ -15,6 +15,7 @@ import (
"testing"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
)
@ -26,30 +27,31 @@ type TestExpectedResources struct {
}
func CompareResources(actualDeployment *appsv1.Deployment, expected TestExpectedResources, t *testing.T) {
container := &actualDeployment.Spec.Template.Spec.Containers[0]
compareQuantity(
"Memory limits",
actualDeployment.Spec.Template.Spec.Containers[0].Resources.Limits.Memory(),
container.Resources.Limits.Memory(),
expected.MemoryLimit,
t,
)
compareQuantity(
"Memory requests",
actualDeployment.Spec.Template.Spec.Containers[0].Resources.Requests.Memory(),
container.Resources.Requests.Memory(),
expected.MemoryRequest,
t,
)
compareQuantity(
"CPU limits",
actualDeployment.Spec.Template.Spec.Containers[0].Resources.Limits.Cpu(),
container.Resources.Limits.Cpu(),
expected.CpuLimit,
t,
)
compareQuantity(
"CPU requests",
actualDeployment.Spec.Template.Spec.Containers[0].Resources.Requests.Cpu(),
container.Resources.Requests.Cpu(),
expected.CpuRequest,
t,
)
@ -80,3 +82,23 @@ func ValidateContainData(actualData map[string]string, expectedData map[string]s
}
}
}
func FindVolume(volumes []corev1.Volume, name string) corev1.Volume {
for _, volume := range volumes {
if volume.Name == name {
return volume
}
}
return corev1.Volume{}
}
func FindVolumeMount(volumes []corev1.VolumeMount, name string) corev1.VolumeMount {
for _, volumeMount := range volumes {
if volumeMount.Name == name {
return volumeMount
}
}
return corev1.VolumeMount{}
}

View File

@ -410,3 +410,14 @@ func GetResourceQuantity(value string, defaultValue string) resource.Quantity {
}
return resource.MustParse(defaultValue)
}
// Finds Env by a given name
func FindEnv(envs []corev1.EnvVar, name string) *corev1.EnvVar {
for _, env := range envs {
if env.Name == name {
return &env
}
}
return nil
}