Automate TLS secrets generation for Kubernetes family infrastructures (#220)
* Automate TLS secrets generation for Kubernetes family infrastructures Signed-off-by: Mykola Morhun <mmorhun@redhat.com>pull/240/head
parent
e655435d5d
commit
bc47b7b1af
|
|
@ -51,6 +51,8 @@ spec:
|
|||
value: quay.io/eclipse/che-plugin-registry:7.12.0
|
||||
- name: IMAGE_default_devfile_registry
|
||||
value: quay.io/eclipse/che-devfile-registry:7.12.0
|
||||
- name: IMAGE_default_che_tls_secrets_creation_job
|
||||
value: quay.io/eclipse/che-tls-secret-creator:alpine-3029769
|
||||
- name: IMAGE_default_pvc_jobs
|
||||
value: registry.access.redhat.com/ubi8-minimal:8.1-409
|
||||
- name: IMAGE_default_postgres
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@ spec:
|
|||
value: quay.io/eclipse/che-plugin-registry:7.12.0
|
||||
- name: IMAGE_default_devfile_registry
|
||||
value: quay.io/eclipse/che-devfile-registry:7.12.0
|
||||
- name: IMAGE_default_che_tls_secrets_creation_job
|
||||
value: quay.io/eclipse/che-tls-secret-creator:alpine-3029769
|
||||
- name: IMAGE_default_pvc_jobs
|
||||
value: registry.access.redhat.com/ubi8-minimal:8.1-409
|
||||
- name: IMAGE_default_postgres
|
||||
|
|
|
|||
|
|
@ -20,6 +20,12 @@ rules:
|
|||
- ingresses
|
||||
verbs:
|
||||
- '*'
|
||||
- apiGroups:
|
||||
- batch
|
||||
resources:
|
||||
- jobs
|
||||
verbs:
|
||||
- '*'
|
||||
- apiGroups:
|
||||
- route.openshift.io
|
||||
resources:
|
||||
|
|
|
|||
|
|
@ -273,6 +273,26 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e
|
|||
}
|
||||
}
|
||||
|
||||
// Handle Che TLS certificates on Kubernetes like infrastructures
|
||||
if instance.Spec.Server.TlsSupport && instance.Spec.Server.SelfSignedCert && !isOpenShift {
|
||||
// Ensure TLS configuration is correct
|
||||
if err := CheckAndUpdateTLSConfiguration(instance, clusterAPI); err != nil {
|
||||
instance, _ = r.GetCR(request)
|
||||
return reconcile.Result{Requeue: true, RequeueAfter: time.Second * 1}, err
|
||||
}
|
||||
|
||||
// Create TLS secrets if needed
|
||||
result, err := HandleCheTLSSecrets(instance, clusterAPI)
|
||||
if result.Requeue || result.RequeueAfter > 0 {
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
if !tests {
|
||||
return result, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get custom ConfigMap
|
||||
// if it exists, add the data into CustomCheProperties
|
||||
customConfigMap := &corev1.ConfigMap{}
|
||||
|
|
@ -401,44 +421,97 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e
|
|||
// create service accounts:
|
||||
// che is the one which token is used to create workspace objects
|
||||
// che-workspace is SA used by plugins like exec and terminal with limited privileges
|
||||
cheServiceAccount := deploy.NewServiceAccount(instance, "che")
|
||||
if err := r.CreateServiceAccount(instance, cheServiceAccount); err != nil {
|
||||
return reconcile.Result{}, err
|
||||
cheSA, err := deploy.SyncServiceAccountToCluster(instance, "che", clusterAPI)
|
||||
if cheSA == nil {
|
||||
logrus.Info("Waiting on service account 'che' to be created")
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
if !tests {
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
}
|
||||
workspaceServiceAccount := deploy.NewServiceAccount(instance, "che-workspace")
|
||||
if err := r.CreateServiceAccount(instance, workspaceServiceAccount); err != nil {
|
||||
return reconcile.Result{}, err
|
||||
|
||||
cheWorkspaceSA, err := deploy.SyncServiceAccountToCluster(instance, "che-workspace", clusterAPI)
|
||||
if cheWorkspaceSA == nil {
|
||||
logrus.Info("Waiting on service account 'che-workspace' to be created")
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
if !tests {
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
}
|
||||
|
||||
// create exec and view roles for CheCluster server and workspaces
|
||||
execRole := deploy.NewRole(instance, "exec", []string{"pods/exec"}, []string{"*"})
|
||||
if err := r.CreateNewRole(instance, execRole); err != nil {
|
||||
return reconcile.Result{}, err
|
||||
role, err := deploy.SyncRoleToCluster(instance, "exec", []string{"pods/exec"}, []string{"*"}, clusterAPI)
|
||||
if role == nil {
|
||||
logrus.Info("Waiting on role 'exec' to be created")
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
if !tests {
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
}
|
||||
viewRole := deploy.NewRole(instance, "view", []string{"pods"}, []string{"list"})
|
||||
if err := r.CreateNewRole(instance, viewRole); err != nil {
|
||||
return reconcile.Result{}, err
|
||||
|
||||
viewRole, err := deploy.SyncRoleToCluster(instance, "view", []string{"pods"}, []string{"list"}, clusterAPI)
|
||||
if viewRole == nil {
|
||||
logrus.Info("Waiting on role 'view' to be created")
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
if !tests {
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
}
|
||||
// create RoleBindings for created (and existing ClusterRole) roles and service accounts
|
||||
cheRoleBinding := deploy.NewRoleBinding(instance, "che", cheServiceAccount.Name, "edit", "ClusterRole")
|
||||
if err := r.CreateNewRoleBinding(instance, cheRoleBinding); err != nil {
|
||||
return reconcile.Result{}, err
|
||||
|
||||
cheRoleBinding, err := deploy.SyncRoleBindingToCluster(instance, "che", "che", "edit", "ClusterRole", clusterAPI)
|
||||
if cheRoleBinding == nil {
|
||||
logrus.Info("Waiting on role binding 'che' to be created")
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
if !tests {
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
}
|
||||
execRoleBinding := deploy.NewRoleBinding(instance, "che-workspace-exec", workspaceServiceAccount.Name, execRole.Name, "Role")
|
||||
if err = r.CreateNewRoleBinding(instance, execRoleBinding); err != nil {
|
||||
return reconcile.Result{}, err
|
||||
|
||||
cheWSExecRoleBinding, err := deploy.SyncRoleBindingToCluster(instance, "che-workspace-exec", "che-workspace", "exec", "Role", clusterAPI)
|
||||
if cheWSExecRoleBinding == nil {
|
||||
logrus.Info("Waiting on role binding 'che-workspace-exec' to be created")
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
if !tests {
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
}
|
||||
viewRoleBinding := deploy.NewRoleBinding(instance, "che-workspace-view", workspaceServiceAccount.Name, viewRole.Name, "Role")
|
||||
if err := r.CreateNewRoleBinding(instance, viewRoleBinding); err != nil {
|
||||
return reconcile.Result{}, err
|
||||
|
||||
cheWSViewRoleBinding, err := deploy.SyncRoleBindingToCluster(instance, "che-workspace-view", "che-workspace", "view", "Role", clusterAPI)
|
||||
if cheWSViewRoleBinding == nil {
|
||||
logrus.Info("Waiting on role binding 'che-workspace-view' to be created")
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
if !tests {
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
}
|
||||
|
||||
// If the user specified an additional cluster role to use for the Che workspace, create a role binding for it
|
||||
// Use a role binding instead of a cluster role binding to keep the additional access scoped to the workspace's namespace
|
||||
workspaceClusterRole := instance.Spec.Server.CheWorkspaceClusterRole
|
||||
if workspaceClusterRole != "" {
|
||||
customRoleBinding := deploy.NewRoleBinding(instance, "che-workspace-custom", workspaceServiceAccount.Name, workspaceClusterRole, "ClusterRole")
|
||||
if err = r.CreateNewRoleBinding(instance, customRoleBinding); err != nil {
|
||||
return reconcile.Result{}, err
|
||||
cheWSCustomRoleBinding, err := deploy.SyncRoleBindingToCluster(instance, "che-workspace-custom", "view", workspaceClusterRole, "ClusterRole", clusterAPI)
|
||||
if cheWSCustomRoleBinding == nil {
|
||||
logrus.Info("Waiting on role binding 'che-workspace-custom' to be created")
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
if !tests {
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -782,10 +855,6 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e
|
|||
}
|
||||
}
|
||||
guessedDevfileRegistryURL := protocol + "://" + host
|
||||
|
||||
if err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
if devfileRegistryURL == "" {
|
||||
devfileRegistryURL = guessedDevfileRegistryURL
|
||||
}
|
||||
|
|
@ -942,9 +1011,6 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e
|
|||
}
|
||||
|
||||
guessedPluginRegistryURL := protocol + "://" + host
|
||||
if err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
guessedPluginRegistryURL += "/v3"
|
||||
if pluginRegistryURL == "" {
|
||||
pluginRegistryURL = guessedPluginRegistryURL
|
||||
|
|
|
|||
|
|
@ -21,54 +21,12 @@ import (
|
|||
oauth "github.com/openshift/api/oauth/v1"
|
||||
"github.com/sirupsen/logrus"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
rbac "k8s.io/api/rbac/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
)
|
||||
|
||||
func (r *ReconcileChe) CreateServiceAccount(cr *orgv1.CheCluster, serviceAccount *corev1.ServiceAccount) error {
|
||||
if err := controllerutil.SetControllerReference(cr, serviceAccount, r.scheme); err != nil {
|
||||
return err
|
||||
}
|
||||
serviceAccountFound := &corev1.ServiceAccount{}
|
||||
err := r.client.Get(context.TODO(), types.NamespacedName{Name: serviceAccount.Name, Namespace: serviceAccount.Namespace}, serviceAccountFound)
|
||||
if err != nil && errors.IsNotFound(err) {
|
||||
logrus.Infof("Creating a new object: %s, name: %s", serviceAccount.Kind, serviceAccount.Name)
|
||||
err = r.client.Create(context.TODO(), serviceAccount)
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to create %s %s: %s", serviceAccount.Name, serviceAccount.Kind, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ReconcileChe) CreateNewRole(instance *orgv1.CheCluster, role *rbac.Role) error {
|
||||
if err := controllerutil.SetControllerReference(instance, role, r.scheme); err != nil {
|
||||
return err
|
||||
}
|
||||
roleFound := &rbac.Role{}
|
||||
err := r.client.Get(context.TODO(), types.NamespacedName{Name: role.Name, Namespace: role.Namespace}, roleFound)
|
||||
if err != nil && errors.IsNotFound(err) {
|
||||
logrus.Infof("Creating a new object: %s, name: %s", role.Kind, role.Name)
|
||||
err = r.client.Create(context.TODO(), role)
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to create %s %s: %s", role.Name, role.Kind, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
} else if err != nil {
|
||||
logrus.Errorf("An error occurred: %s", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ReconcileChe) CreateNewSecret(instance *orgv1.CheCluster, secret *corev1.Secret) error {
|
||||
if err := controllerutil.SetControllerReference(instance, secret, r.scheme); err != nil {
|
||||
logrus.Errorf("An error occurred: %s", err)
|
||||
|
|
@ -111,27 +69,6 @@ func (r *ReconcileChe) CreateNewOauthClient(instance *orgv1.CheCluster, oAuthCli
|
|||
return nil
|
||||
}
|
||||
|
||||
func (r *ReconcileChe) CreateNewRoleBinding(instance *orgv1.CheCluster, roleBinding *rbac.RoleBinding) error {
|
||||
if err := controllerutil.SetControllerReference(instance, roleBinding, r.scheme); err != nil {
|
||||
return err
|
||||
}
|
||||
roleBindingFound := &rbac.RoleBinding{}
|
||||
err := r.client.Get(context.TODO(), types.NamespacedName{Name: roleBinding.Name, Namespace: roleBinding.Namespace}, roleBindingFound)
|
||||
if err != nil && errors.IsNotFound(err) {
|
||||
logrus.Infof("Creating a new object: %s, name: %s", roleBinding.Kind, roleBinding.Name)
|
||||
err = r.client.Create(context.TODO(), roleBinding)
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to create %s %s: %s", roleBinding.Name, roleBinding.Kind, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
} else if err != nil {
|
||||
logrus.Errorf("An error occurred: %s", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ReconcileChe) CreateIdentityProviderItems(instance *orgv1.CheCluster, request reconcile.Request, cheFlavor string, keycloakDeploymentName string, isOpenShift4 bool) (err error) {
|
||||
tests := r.tests
|
||||
oAuthClientName := instance.Spec.Auth.OAuthClientName
|
||||
|
|
|
|||
|
|
@ -0,0 +1,262 @@
|
|||
//
|
||||
// Copyright (c) 2020 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 che
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
orgv1 "github.com/eclipse/che-operator/pkg/apis/org/v1"
|
||||
"github.com/eclipse/che-operator/pkg/deploy"
|
||||
"github.com/sirupsen/logrus"
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
)
|
||||
|
||||
const (
|
||||
CheTLSJobServiceAccountName = "che-tls-job-service-account"
|
||||
CheTLSJobRoleName = "che-tls-job-role"
|
||||
CheTLSJobRoleBindingName = "che-tls-job-role-binding"
|
||||
CheTLSJobName = "che-tls-job"
|
||||
CheTlsJobComponentName = "che-create-tls-secret-job"
|
||||
CheTLSSelfSignedCertificateSecretName = "self-signed-certificate"
|
||||
)
|
||||
|
||||
// HandleCheTLSSecrets handles TLS secrets required for Che deployment
|
||||
func HandleCheTLSSecrets(checluster *orgv1.CheCluster, clusterAPI deploy.ClusterAPI) (reconcile.Result, error) {
|
||||
cheTLSSecretName := checluster.Spec.K8s.TlsSecretName
|
||||
|
||||
// ===== Check Che TLS certificate ===== //
|
||||
cheTLSSecret := &corev1.Secret{}
|
||||
err := clusterAPI.Client.Get(context.TODO(), types.NamespacedName{Namespace: checluster.Namespace, Name: cheTLSSecretName}, cheTLSSecret)
|
||||
if err != nil {
|
||||
if !errors.IsNotFound(err) {
|
||||
// Error reading secret info
|
||||
logrus.Errorf("Error getting Che TLS secert \"%s\": %v", cheTLSSecretName, err)
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
|
||||
// Che TLS secret doesn't exist, generate a new one
|
||||
|
||||
// Remove Che CA certificate secret if any
|
||||
cheCASelfSignedCertificateSecret := &corev1.Secret{}
|
||||
err = clusterAPI.Client.Get(context.TODO(), types.NamespacedName{Namespace: checluster.Namespace, Name: CheTLSSelfSignedCertificateSecretName}, cheCASelfSignedCertificateSecret)
|
||||
if err != nil {
|
||||
if !errors.IsNotFound(err) {
|
||||
// Error reading secret info
|
||||
logrus.Errorf("Error getting Che self-signed certificate secert \"%s\": %v", CheTLSSelfSignedCertificateSecretName, err)
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
// Che CA certificate doesn't exists (that's expected at this point), do nothing
|
||||
} else {
|
||||
// Remove Che CA secret because Che TLS secret is missing (they should be generated together).
|
||||
if err = clusterAPI.Client.Delete(context.TODO(), cheCASelfSignedCertificateSecret); err != nil {
|
||||
logrus.Errorf("Error deleting Che self-signed certificate secret \"%s\": %v", CheTLSSelfSignedCertificateSecretName, err)
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare permissions for the certificate generation job
|
||||
sa, err := deploy.SyncServiceAccountToCluster(checluster, CheTLSJobServiceAccountName, clusterAPI)
|
||||
if sa == nil {
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
|
||||
role, err := deploy.SyncRoleToCluster(checluster, CheTLSJobRoleName, []string{"secrets"}, []string{"create"}, clusterAPI)
|
||||
if role == nil {
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
|
||||
roleBiding, err := deploy.SyncRoleBindingToCluster(checluster, CheTLSJobRoleBindingName, CheTLSJobServiceAccountName, CheTLSJobRoleName, "Role", clusterAPI)
|
||||
if roleBiding == nil {
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
|
||||
cheTLSSecretsCreationJobImage := deploy.DefaultCheTLSSecretsCreationJobImage()
|
||||
jobEnvVars := map[string]string{
|
||||
"DOMAIN": checluster.Spec.K8s.IngressDomain,
|
||||
"CHE_NAMESPACE": checluster.Namespace,
|
||||
"CHE_SERVER_TLS_SECRET_NAME": cheTLSSecretName,
|
||||
"CHE_CA_CERTIFICATE_SECRET_NAME": CheTLSSelfSignedCertificateSecretName,
|
||||
}
|
||||
job, err := deploy.SyncJobToCluster(checluster, CheTLSJobName, CheTlsJobComponentName, cheTLSSecretsCreationJobImage, CheTLSJobServiceAccountName, jobEnvVars, clusterAPI)
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
if job == nil || job.Status.Succeeded == 0 {
|
||||
logrus.Infof("Waiting on job '%s' to be finished", CheTLSJobName)
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
}
|
||||
|
||||
// cleanup job
|
||||
job := &batchv1.Job{}
|
||||
err = clusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: CheTLSJobName, Namespace: checluster.Namespace}, job)
|
||||
if err != nil && !errors.IsNotFound(err) {
|
||||
// Failed to get the job
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
if err == nil {
|
||||
// The job object is present
|
||||
if job.Status.Succeeded > 0 {
|
||||
logrus.Infof("Import public part of Eclipse Che self-signed CA certificvate from \"%s\" secret into your browser.", CheTLSSelfSignedCertificateSecretName)
|
||||
deleteJob(job, checluster, clusterAPI)
|
||||
} else if job.Status.Failed > 0 {
|
||||
// The job failed, but the certificate is present, shouldn't happen
|
||||
deleteJob(job, checluster, clusterAPI)
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
// Job hasn't reported finished status yet, wait more
|
||||
return reconcile.Result{RequeueAfter: time.Second}, nil
|
||||
}
|
||||
|
||||
// Che TLS certificate exists, check for required data fields
|
||||
if !isCheTLSSecretValid(cheTLSSecret) {
|
||||
// The secret is invalid because required field(s) missing.
|
||||
logrus.Infof("Che TLS secret \"%s\" is invalid. Recreating...", cheTLSSecretName)
|
||||
// Delete old invalid secret
|
||||
if err = clusterAPI.Client.Delete(context.TODO(), cheTLSSecret); err != nil {
|
||||
logrus.Errorf("Error deleting Che TLS secret \"%s\": %v", cheTLSSecretName, err)
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
// Recreate the secret
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
|
||||
// Check owner reference
|
||||
if cheTLSSecret.ObjectMeta.OwnerReferences == nil {
|
||||
// Set owner Che cluster as Che TLS secret owner
|
||||
if err := controllerutil.SetControllerReference(checluster, cheTLSSecret, clusterAPI.Scheme); err != nil {
|
||||
logrus.Errorf("Failed to set owner for Che TLS secret \"%s\". Error: %s", cheTLSSecretName, err)
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
if err := clusterAPI.Client.Update(context.TODO(), cheTLSSecret); err != nil {
|
||||
logrus.Errorf("Failed to update owner for Che TLS secret \"%s\". Error: %s", cheTLSSecretName, err)
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Check Che CA certificate ===== //
|
||||
|
||||
cheTLSSelfSignedCertificateSecret := &corev1.Secret{}
|
||||
err = clusterAPI.Client.Get(context.TODO(), types.NamespacedName{Namespace: checluster.Namespace, Name: CheTLSSelfSignedCertificateSecretName}, cheTLSSelfSignedCertificateSecret)
|
||||
if err != nil {
|
||||
if !errors.IsNotFound(err) {
|
||||
// Error reading Che self-signed secret info
|
||||
logrus.Errorf("Error getting Che self-signed certificate secert \"%s\": %v", CheTLSSelfSignedCertificateSecretName, err)
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
// Che CA self-signed cetificate secret doesn't exist.
|
||||
// Such situation could happen between reconcile loops, when CA cert is deleted.
|
||||
// However the certificates should be created together,
|
||||
// so it is mandatory to remove Che TLS secret now and recreate the pair.
|
||||
cheTLSSecret = &corev1.Secret{}
|
||||
err = clusterAPI.Client.Get(context.TODO(), types.NamespacedName{Namespace: checluster.Namespace, Name: cheTLSSecretName}, cheTLSSecret)
|
||||
if err != nil { // No need to check for not found error as the secret already exists at this point
|
||||
logrus.Errorf("Error getting Che TLS secert \"%s\": %v", cheTLSSecretName, err)
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
if err = clusterAPI.Client.Delete(context.TODO(), cheTLSSecret); err != nil {
|
||||
logrus.Errorf("Error deleting Che TLS secret \"%s\": %v", cheTLSSecretName, err)
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
// Invalid secrets cleaned up, recreate them now
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
|
||||
// Che CA self-signed certificate secret exists, check for required data fields
|
||||
if !isCheCASecretValid(cheTLSSelfSignedCertificateSecret) {
|
||||
logrus.Infof("Che self-signed certificate secret \"%s\" is invalid. Recrating...", CheTLSSelfSignedCertificateSecretName)
|
||||
// Che CA self-signed certificate secret is invalid, delete it
|
||||
if err = clusterAPI.Client.Delete(context.TODO(), cheTLSSelfSignedCertificateSecret); err != nil {
|
||||
logrus.Errorf("Error deleting Che self-signed certificate secret \"%s\": %v", CheTLSSelfSignedCertificateSecretName, err)
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
// Also delete Che TLS as the certificates should be created together
|
||||
// Here it is not mandatory to check Che TLS secret existence as it is handled above
|
||||
if err = clusterAPI.Client.Delete(context.TODO(), cheTLSSecret); err != nil {
|
||||
logrus.Errorf("Error deleting Che TLS secret \"%s\": %v", cheTLSSecretName, err)
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
// Regenerate Che TLS certicates and recreate secrets
|
||||
return reconcile.Result{RequeueAfter: time.Second}, nil
|
||||
}
|
||||
|
||||
// Check owner reference
|
||||
if cheTLSSelfSignedCertificateSecret.ObjectMeta.OwnerReferences == nil {
|
||||
// Set owner Che cluster as Che TLS secret owner
|
||||
if err := controllerutil.SetControllerReference(checluster, cheTLSSelfSignedCertificateSecret, clusterAPI.Scheme); err != nil {
|
||||
logrus.Errorf("Failed to set owner for Che self-signed certificate secret \"%s\". Error: %s", CheTLSSelfSignedCertificateSecretName, err)
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
if err := clusterAPI.Client.Update(context.TODO(), cheTLSSelfSignedCertificateSecret); err != nil {
|
||||
logrus.Errorf("Failed to update owner for Che self-signed certificate secret \"%s\". Error: %s", CheTLSSelfSignedCertificateSecretName, err)
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
}
|
||||
|
||||
// Both secrets are ok, go further in reconcile loop
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
|
||||
func isCheTLSSecretValid(cheTLSSecret *corev1.Secret) bool {
|
||||
if data, exists := cheTLSSecret.Data["tls.key"]; !exists || len(data) == 0 {
|
||||
return false
|
||||
}
|
||||
if data, exists := cheTLSSecret.Data["tls.crt"]; !exists || len(data) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func isCheCASecretValid(cheCASelfSignedCertificateSecret *corev1.Secret) bool {
|
||||
if data, exists := cheCASelfSignedCertificateSecret.Data["ca.crt"]; !exists || len(data) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// CheckAndUpdateTLSConfiguration validates TLS configuration and precreated objects if any
|
||||
// If configuration is wrong it will guess most common use cases and will make changes in Che CR accordingly to the assumption.
|
||||
func CheckAndUpdateTLSConfiguration(checluster *orgv1.CheCluster, clusterAPI deploy.ClusterAPI) error {
|
||||
if checluster.Spec.K8s.TlsSecretName == "" {
|
||||
checluster.Spec.K8s.TlsSecretName = "che-tls"
|
||||
if err := deploy.UpdateCheCRSpec(checluster, "TlsSecretName", checluster.Spec.K8s.TlsSecretName, clusterAPI); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteJob(job *batchv1.Job, checluster *orgv1.CheCluster, clusterAPI deploy.ClusterAPI) {
|
||||
names := k8sclient.GetPodsByComponent(CheTlsJobComponentName, checluster.Namespace)
|
||||
for _, podName := range names {
|
||||
pod := &corev1.Pod{}
|
||||
err := clusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: podName, Namespace: checluster.Namespace}, pod)
|
||||
if err == nil {
|
||||
// Delete pod (for some reasons pod isn't removed when job is removed)
|
||||
if err = clusterAPI.Client.Delete(context.TODO(), pod); err != nil {
|
||||
logrus.Errorf("Error deleting pod: '%s', error: %v", podName, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := clusterAPI.Client.Delete(context.TODO(), job); err != nil {
|
||||
logrus.Errorf("Error deleting job: '%s', error: %v", CheTLSJobName, err)
|
||||
}
|
||||
}
|
||||
|
|
@ -27,13 +27,14 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
defaultCheServerImage string
|
||||
defaultCheVersion string
|
||||
defaultPluginRegistryImage string
|
||||
defaultDevfileRegistryImage string
|
||||
defaultPvcJobsImage string
|
||||
defaultPostgresImage string
|
||||
defaultKeycloakImage string
|
||||
defaultCheServerImage string
|
||||
defaultCheVersion string
|
||||
defaultPluginRegistryImage string
|
||||
defaultDevfileRegistryImage string
|
||||
defaultCheTLSSecretsCreationJobImage string
|
||||
defaultPvcJobsImage string
|
||||
defaultPostgresImage string
|
||||
defaultKeycloakImage string
|
||||
|
||||
defaultCheWorkspacePluginBrokerMetadataImage string
|
||||
defaultCheWorkspacePluginBrokerArtifactsImage string
|
||||
|
|
@ -114,6 +115,7 @@ func InitDefaultsFromEnv() {
|
|||
defaultCheServerImage = getDefaultFromEnv("IMAGE_default_che_server")
|
||||
defaultPluginRegistryImage = getDefaultFromEnv("IMAGE_default_plugin_registry")
|
||||
defaultDevfileRegistryImage = getDefaultFromEnv("IMAGE_default_devfile_registry")
|
||||
defaultCheTLSSecretsCreationJobImage = getDefaultFromEnv("IMAGE_default_che_tls_secrets_creation_job")
|
||||
defaultPvcJobsImage = getDefaultFromEnv("IMAGE_default_pvc_jobs")
|
||||
defaultPostgresImage = getDefaultFromEnv("IMAGE_default_postgres")
|
||||
defaultKeycloakImage = getDefaultFromEnv("IMAGE_default_keycloak")
|
||||
|
|
@ -133,6 +135,7 @@ func InitDefaultsFromFile(defaultsPath string) {
|
|||
defaultCheServerImage = util.GetDeploymentEnv(operatorDeployment, "IMAGE_default_che_server")
|
||||
defaultPluginRegistryImage = util.GetDeploymentEnv(operatorDeployment, "IMAGE_default_plugin_registry")
|
||||
defaultDevfileRegistryImage = util.GetDeploymentEnv(operatorDeployment, "IMAGE_default_devfile_registry")
|
||||
defaultCheTLSSecretsCreationJobImage = util.GetDeploymentEnv(operatorDeployment, "IMAGE_default_che_tls_secrets_creation_job")
|
||||
defaultPvcJobsImage = util.GetDeploymentEnv(operatorDeployment, "IMAGE_default_pvc_jobs")
|
||||
defaultPostgresImage = util.GetDeploymentEnv(operatorDeployment, "IMAGE_default_postgres")
|
||||
defaultKeycloakImage = util.GetDeploymentEnv(operatorDeployment, "IMAGE_default_keycloak")
|
||||
|
|
@ -195,6 +198,10 @@ func DefaultCheServerImage(cr *orgv1.CheCluster) string {
|
|||
return patchDefaultImageName(cr, defaultCheServerImage)
|
||||
}
|
||||
|
||||
func DefaultCheTLSSecretsCreationJobImage() string {
|
||||
return defaultCheTLSSecretsCreationJobImage
|
||||
}
|
||||
|
||||
func DefaultPvcJobsImage(cr *orgv1.CheCluster) string {
|
||||
return patchDefaultImageName(cr, defaultPvcJobsImage)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,16 +20,17 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
cheVersionTest = "7.12.0"
|
||||
cheServerImageTest = "quay.io/eclipse/che-server:7.12.0"
|
||||
pluginRegistryImageTest = "quay.io/eclipse/che-plugin-registry:7.12.0"
|
||||
devfileRegistryImageTest = "quay.io/eclipse/che-devfile-registry:7.12.0"
|
||||
pvcJobsImageTest = "registry.access.redhat.com/ubi8-minimal:8.1-409"
|
||||
postgresImageTest = "centos/postgresql-96-centos7:9.6"
|
||||
keycloakImageTest = "quay.io/eclipse/che-keycloak:7.12.0"
|
||||
brokerMetadataTest = "quay.io/eclipse/che-plugin-metadata-broker:v3.1.2"
|
||||
brokerArtifactsTest = "quay.io/eclipse/che-plugin-artifacts-broker:v3.1.2"
|
||||
jwtProxyTest = "quay.io/eclipse/che-jwtproxy:fd94e60"
|
||||
cheVersionTest = "7.12.0"
|
||||
cheServerImageTest = "quay.io/eclipse/che-server:7.12.0"
|
||||
pluginRegistryImageTest = "quay.io/eclipse/che-plugin-registry:7.12.0"
|
||||
devfileRegistryImageTest = "quay.io/eclipse/che-devfile-registry:7.12.0"
|
||||
cheTLSSecretsCeationJobImageTest = "quay.io/eclipse/che-tls-secrets-creation-job:7.12.0"
|
||||
pvcJobsImageTest = "registry.access.redhat.com/ubi8-minimal:8.1-409"
|
||||
postgresImageTest = "centos/postgresql-96-centos7:9.6"
|
||||
keycloakImageTest = "quay.io/eclipse/che-keycloak:7.12.0"
|
||||
brokerMetadataTest = "quay.io/eclipse/che-plugin-metadata-broker:v3.1.2"
|
||||
brokerArtifactsTest = "quay.io/eclipse/che-plugin-artifacts-broker:v3.1.2"
|
||||
jwtProxyTest = "quay.io/eclipse/che-jwtproxy:fd94e60"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
@ -37,6 +38,7 @@ func init() {
|
|||
os.Setenv("IMAGE_default_che_server", cheServerImageTest)
|
||||
os.Setenv("IMAGE_default_plugin_registry", pluginRegistryImageTest)
|
||||
os.Setenv("IMAGE_default_devfile_registry", devfileRegistryImageTest)
|
||||
os.Setenv("IMAGE_default_che_tls_secrets_creation_job", cheTLSSecretsCeationJobImageTest)
|
||||
os.Setenv("IMAGE_default_pvc_jobs", pvcJobsImageTest)
|
||||
os.Setenv("IMAGE_default_postgres", postgresImageTest)
|
||||
os.Setenv("IMAGE_default_keycloak", keycloakImageTest)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,174 @@
|
|||
//
|
||||
// Copyright (c) 2020 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 (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
orgv1 "github.com/eclipse/che-operator/pkg/apis/org/v1"
|
||||
"github.com/eclipse/che-operator/pkg/util"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/sirupsen/logrus"
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
)
|
||||
|
||||
var (
|
||||
jobDiffOpts = cmp.Options{
|
||||
cmpopts.IgnoreFields(batchv1.Job{}, "TypeMeta", "ObjectMeta", "Status"),
|
||||
cmpopts.IgnoreFields(batchv1.JobSpec{}, "Selector", "TTLSecondsAfterFinished"),
|
||||
cmpopts.IgnoreFields(v1.PodTemplateSpec{}, "ObjectMeta"),
|
||||
cmpopts.IgnoreFields(corev1.Container{}, "TerminationMessagePath", "TerminationMessagePolicy"),
|
||||
cmpopts.IgnoreFields(corev1.PodSpec{}, "DNSPolicy", "SchedulerName", "SecurityContext"),
|
||||
cmp.Comparer(func(x, y []corev1.EnvVar) bool {
|
||||
xMap := make(map[string]string)
|
||||
yMap := make(map[string]string)
|
||||
for _, env := range x {
|
||||
xMap[env.Name] = env.Value
|
||||
}
|
||||
for _, env := range y {
|
||||
yMap[env.Name] = env.Value
|
||||
}
|
||||
return reflect.DeepEqual(xMap, yMap)
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
||||
func SyncJobToCluster(
|
||||
checluster *orgv1.CheCluster,
|
||||
name string,
|
||||
component string,
|
||||
image string,
|
||||
serviceAccountName string,
|
||||
env map[string]string,
|
||||
clusterAPI ClusterAPI) (*batchv1.Job, error) {
|
||||
|
||||
specJob, err := getSpecJob(checluster, name, component, image, serviceAccountName, env, clusterAPI)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clusterJob, err := getClusterJob(specJob.Name, specJob.Namespace, clusterAPI)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if clusterJob == nil {
|
||||
logrus.Infof("Creating a new object: %s, name %s", specJob.Kind, specJob.Name)
|
||||
err := clusterAPI.Client.Create(context.TODO(), specJob)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
diff := cmp.Diff(clusterJob, specJob, jobDiffOpts)
|
||||
if len(diff) > 0 {
|
||||
logrus.Infof("Updating existed object: %s, name: %s", clusterJob.Kind, clusterJob.Name)
|
||||
fmt.Printf("Difference:\n%s", diff)
|
||||
|
||||
if err := clusterAPI.Client.Delete(context.TODO(), clusterJob); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err := clusterAPI.Client.Create(context.TODO(), specJob)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return clusterJob, nil
|
||||
}
|
||||
|
||||
// GetSpecJob creates new job configuration by given parameters.
|
||||
func getSpecJob(
|
||||
checluster *orgv1.CheCluster,
|
||||
name string,
|
||||
component string,
|
||||
image string,
|
||||
serviceAccountName string,
|
||||
env map[string]string,
|
||||
clusterAPI ClusterAPI) (*batchv1.Job, error) {
|
||||
labels := GetLabels(checluster, util.GetValue(checluster.Spec.Server.CheFlavor, DefaultCheFlavor))
|
||||
labels["component"] = component
|
||||
|
||||
backoffLimit := int32(3)
|
||||
parallelism := int32(1)
|
||||
comletions := int32(1)
|
||||
terminationGracePeriodSeconds := int64(30)
|
||||
ttlSecondsAfterFinished := int32(30)
|
||||
pullPolicy := corev1.PullPolicy(util.GetValue(string(checluster.Spec.Server.CheImagePullPolicy), "IfNotPresent"))
|
||||
|
||||
var jobEnvVars []corev1.EnvVar
|
||||
for envVarName, envVarValue := range env {
|
||||
jobEnvVars = append(jobEnvVars, corev1.EnvVar{Name: envVarName, Value: envVarValue})
|
||||
}
|
||||
|
||||
job := &batchv1.Job{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Job",
|
||||
APIVersion: batchv1.SchemeGroupVersion.String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: checluster.Namespace,
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: batchv1.JobSpec{
|
||||
Template: corev1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: corev1.PodSpec{
|
||||
ServiceAccountName: serviceAccountName,
|
||||
DeprecatedServiceAccount: serviceAccountName,
|
||||
RestartPolicy: "Never",
|
||||
TerminationGracePeriodSeconds: &terminationGracePeriodSeconds,
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: name + "-job-container",
|
||||
Image: image,
|
||||
ImagePullPolicy: pullPolicy,
|
||||
Env: jobEnvVars,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
TTLSecondsAfterFinished: &ttlSecondsAfterFinished,
|
||||
Parallelism: ¶llelism,
|
||||
BackoffLimit: &backoffLimit,
|
||||
Completions: &comletions,
|
||||
},
|
||||
}
|
||||
|
||||
if err := controllerutil.SetControllerReference(checluster, job, clusterAPI.Scheme); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return job, nil
|
||||
}
|
||||
|
||||
// GetClusterJob gets and returns specified job
|
||||
func getClusterJob(name string, namespace string, clusterAPI ClusterAPI) (*batchv1.Job, error) {
|
||||
job := &batchv1.Job{}
|
||||
err := clusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: namespace}, job)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return job, nil
|
||||
}
|
||||
|
|
@ -12,22 +12,71 @@
|
|||
package deploy
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
orgv1 "github.com/eclipse/che-operator/pkg/apis/org/v1"
|
||||
"github.com/eclipse/che-operator/pkg/util"
|
||||
"github.com/sirupsen/logrus"
|
||||
rbac "k8s.io/api/rbac/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
runtimeClient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
)
|
||||
|
||||
func NewRole(cr *orgv1.CheCluster, name string, resources []string, verbs []string) *rbac.Role {
|
||||
labels := GetLabels(cr, util.GetValue(cr.Spec.Server.CheFlavor, DefaultCheFlavor))
|
||||
return &rbac.Role{
|
||||
func SyncRoleToCluster(
|
||||
checluster *orgv1.CheCluster,
|
||||
name string,
|
||||
resources []string,
|
||||
verbs []string,
|
||||
clusterAPI ClusterAPI) (*rbac.Role, error) {
|
||||
|
||||
specRole, err := getSpecRole(checluster, name, resources, verbs, clusterAPI)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clusterRole, err := getClusterRole(specRole.Name, specRole.Namespace, clusterAPI.Client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if clusterRole == nil {
|
||||
logrus.Infof("Creating a new object: %s, name %s", specRole.Kind, specRole.Name)
|
||||
err := clusterAPI.Client.Create(context.TODO(), specRole)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return clusterRole, nil
|
||||
}
|
||||
|
||||
func getClusterRole(name string, namespace string, client runtimeClient.Client) (*rbac.Role, error) {
|
||||
role := &rbac.Role{}
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
}
|
||||
err := client.Get(context.TODO(), namespacedName, role)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return role, nil
|
||||
}
|
||||
|
||||
func getSpecRole(checluster *orgv1.CheCluster, name string, resources []string, verbs []string, clusterAPI ClusterAPI) (*rbac.Role, error) {
|
||||
labels := GetLabels(checluster, util.GetValue(checluster.Spec.Server.CheFlavor, DefaultCheFlavor))
|
||||
role := &rbac.Role{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Role",
|
||||
APIVersion: rbac.SchemeGroupVersion.String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: cr.Namespace,
|
||||
Namespace: checluster.Namespace,
|
||||
Labels: labels,
|
||||
},
|
||||
Rules: []rbac.PolicyRule{
|
||||
|
|
@ -40,7 +89,11 @@ func NewRole(cr *orgv1.CheCluster, name string, resources []string, verbs []stri
|
|||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := controllerutil.SetControllerReference(checluster, role, clusterAPI.Scheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return role, nil
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -12,29 +12,86 @@
|
|||
package deploy
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
orgv1 "github.com/eclipse/che-operator/pkg/apis/org/v1"
|
||||
"github.com/eclipse/che-operator/pkg/util"
|
||||
"github.com/sirupsen/logrus"
|
||||
rbac "k8s.io/api/rbac/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
runtimeClient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
)
|
||||
|
||||
func NewRoleBinding(cr *orgv1.CheCluster, name string, serviceAccountName string, roleName string, roleKind string) *rbac.RoleBinding {
|
||||
labels := GetLabels(cr, util.GetValue(cr.Spec.Server.CheFlavor, DefaultCheFlavor))
|
||||
return &rbac.RoleBinding{
|
||||
func SyncRoleBindingToCluster(
|
||||
checluster *orgv1.CheCluster,
|
||||
name string,
|
||||
serviceAccountName string,
|
||||
roleName string,
|
||||
roleKind string,
|
||||
clusterAPI ClusterAPI) (*rbac.RoleBinding, error) {
|
||||
|
||||
specRB, err := getSpecRoleBinding(checluster, name, serviceAccountName, roleName, roleKind, clusterAPI)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clusterRB, err := getClusterRoleBiding(specRB.Name, specRB.Namespace, clusterAPI.Client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if clusterRB == nil {
|
||||
logrus.Infof("Creating a new object: %s, name %s", specRB.Kind, specRB.Name)
|
||||
err := clusterAPI.Client.Create(context.TODO(), specRB)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return clusterRB, nil
|
||||
}
|
||||
|
||||
func getClusterRoleBiding(name string, namespace string, client runtimeClient.Client) (*rbac.RoleBinding, error) {
|
||||
roleBinding := &rbac.RoleBinding{}
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
}
|
||||
err := client.Get(context.TODO(), namespacedName, roleBinding)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return roleBinding, nil
|
||||
}
|
||||
|
||||
func getSpecRoleBinding(
|
||||
checluster *orgv1.CheCluster,
|
||||
name string,
|
||||
serviceAccountName string,
|
||||
roleName string,
|
||||
roleKind string,
|
||||
clusterAPI ClusterAPI) (*rbac.RoleBinding, error) {
|
||||
|
||||
labels := GetLabels(checluster, util.GetValue(checluster.Spec.Server.CheFlavor, DefaultCheFlavor))
|
||||
roleBinding := &rbac.RoleBinding{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "RoleBinding",
|
||||
APIVersion: rbac.SchemeGroupVersion.String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: cr.Namespace,
|
||||
Namespace: checluster.Namespace,
|
||||
Labels: labels,
|
||||
},
|
||||
Subjects: []rbac.Subject{
|
||||
{
|
||||
Kind: rbac.ServiceAccountKind,
|
||||
Name: serviceAccountName,
|
||||
Namespace: cr.Namespace,
|
||||
Namespace: checluster.Namespace,
|
||||
},
|
||||
},
|
||||
RoleRef: rbac.RoleRef{
|
||||
|
|
@ -43,5 +100,11 @@ func NewRoleBinding(cr *orgv1.CheCluster, name string, serviceAccountName string
|
|||
APIGroup: "rbac.authorization.k8s.io",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
err := controllerutil.SetControllerReference(checluster, roleBinding, clusterAPI.Scheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return roleBinding, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,23 +12,73 @@
|
|||
package deploy
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
orgv1 "github.com/eclipse/che-operator/pkg/apis/org/v1"
|
||||
"github.com/eclipse/che-operator/pkg/util"
|
||||
"github.com/sirupsen/logrus"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
runtimeClient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
)
|
||||
|
||||
func NewServiceAccount(cr *orgv1.CheCluster, name string) *corev1.ServiceAccount {
|
||||
labels := GetLabels(cr, util.GetValue(cr.Spec.Server.CheFlavor, DefaultCheFlavor))
|
||||
return &corev1.ServiceAccount{
|
||||
func SyncServiceAccountToCluster(checluster *orgv1.CheCluster, name string, clusterAPI ClusterAPI) (*corev1.ServiceAccount, error) {
|
||||
specSA, err := getSpecServiceAccount(checluster, name, clusterAPI)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clusterSA, err := getClusterServiceAccount(specSA.Name, specSA.Namespace, clusterAPI.Client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if clusterSA == nil {
|
||||
logrus.Infof("Creating a new object: %s, name %s", specSA.Kind, specSA.Name)
|
||||
err := clusterAPI.Client.Create(context.TODO(), specSA)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return clusterSA, nil
|
||||
}
|
||||
|
||||
func getClusterServiceAccount(name string, namespace string, client runtimeClient.Client) (*corev1.ServiceAccount, error) {
|
||||
serviceAccount := &corev1.ServiceAccount{}
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
}
|
||||
err := client.Get(context.TODO(), namespacedName, serviceAccount)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return serviceAccount, nil
|
||||
}
|
||||
|
||||
func getSpecServiceAccount(checluster *orgv1.CheCluster, name string, clusterAPI ClusterAPI) (*corev1.ServiceAccount, error) {
|
||||
labels := GetLabels(checluster, util.GetValue(checluster.Spec.Server.CheFlavor, DefaultCheFlavor))
|
||||
serviceAccount := &corev1.ServiceAccount{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "ServiceAccount",
|
||||
APIVersion: "v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: cr.Namespace,
|
||||
Namespace: checluster.Namespace,
|
||||
Labels: labels,
|
||||
},
|
||||
}
|
||||
|
||||
err := controllerutil.SetControllerReference(checluster, serviceAccount, clusterAPI.Scheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return serviceAccount, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// 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 (
|
||||
"context"
|
||||
|
||||
orgv1 "github.com/eclipse/che-operator/pkg/apis/org/v1"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func UpdateCheCRSpec(instance *orgv1.CheCluster, updatedField string, value string, clusterAPI ClusterAPI) (err error) {
|
||||
logrus.Infof("Updating %s CR with %s: %s", instance.Name, updatedField, value)
|
||||
err = clusterAPI.Client.Update(context.TODO(), instance)
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to update %s CR: %s", instance.Name, err)
|
||||
return err
|
||||
}
|
||||
logrus.Infof("Custom resource %s updated", instance.Name)
|
||||
return nil
|
||||
}
|
||||
|
|
@ -109,7 +109,7 @@ func (cl *k8s) GetDeploymentPod(name string, ns string) (podName string, err err
|
|||
podList, _ := api.Pods(ns).List(listOptions)
|
||||
podListItems := podList.Items
|
||||
if len(podListItems) == 0 {
|
||||
logrus.Errorf("Failed to find pod to exec into. List of pods: %v", podListItems)
|
||||
logrus.Errorf("Failed to find pod for component %s. List of pods: %v", name, podListItems)
|
||||
return "", err
|
||||
}
|
||||
// expecting only one pod to be there so, taking the first one
|
||||
|
|
@ -118,6 +118,20 @@ func (cl *k8s) GetDeploymentPod(name string, ns string) (podName string, err err
|
|||
return podName, nil
|
||||
}
|
||||
|
||||
func (cl *k8s) GetPodsByComponent(name string, ns string) []string {
|
||||
names := []string{}
|
||||
api := cl.clientset.CoreV1()
|
||||
listOptions := metav1.ListOptions{
|
||||
LabelSelector: "component=" + name,
|
||||
}
|
||||
podList, _ := api.Pods(ns).List(listOptions)
|
||||
for _, pod := range podList.Items {
|
||||
names = append(names, pod.Name)
|
||||
}
|
||||
|
||||
return names
|
||||
}
|
||||
|
||||
// Reads 'user' and 'password' from the given secret
|
||||
func (cl *k8s) ReadSecret(name string, ns string) (user string, password string, err error) {
|
||||
secret, err := cl.clientset.CoreV1().Secrets(ns).Get(name, metav1.GetOptions{})
|
||||
|
|
|
|||
Loading…
Reference in New Issue