checontroller refactoring (#1138)
* chore: Introduce reconcilemanager, update status when reconciliation failed. Signed-off-by: Anatolii Bazko <abazko@redhat.com>pull/1141/head
parent
06aa35c92c
commit
f838cf48d2
|
|
@ -1,44 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2019-2021 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 (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
orgv1 "github.com/eclipse-che/che-operator/api/v1"
|
||||
"github.com/eclipse-che/che-operator/pkg/util"
|
||||
)
|
||||
|
||||
// ValidateCheCR checks Che CR configuration.
|
||||
// It should detect:
|
||||
// - configurations which miss required field(s) to deploy Che
|
||||
// - self-contradictory configurations
|
||||
// - configurations with which it is impossible to deploy Che
|
||||
func ValidateCheCR(checluster *orgv1.CheCluster) error {
|
||||
if !util.IsOpenShift {
|
||||
if checluster.Spec.K8s.IngressDomain == "" {
|
||||
return fmt.Errorf("Required field \"spec.K8s.IngressDomain\" is not set")
|
||||
}
|
||||
}
|
||||
|
||||
workspaceNamespaceDefault := util.GetWorkspaceNamespaceDefault(checluster)
|
||||
if strings.Index(workspaceNamespaceDefault, "<username>") == -1 && strings.Index(workspaceNamespaceDefault, "<userid>") == -1 {
|
||||
return fmt.Errorf(`Namespace strategies other than 'per user' is not supported anymore. Using the <username> or <userid> placeholder is required in the 'spec.server.workspaceNamespaceDefault' field. The current value is: %s`, workspaceNamespaceDefault)
|
||||
}
|
||||
|
||||
if !util.IsCheMultiUser(checluster) {
|
||||
return fmt.Errorf("Single user authentication mode is not supported anymore. To backup your data you can commit workspace configuration to an SCM server and use factories to restore it in multi user mode. To switch to multi user authentication mode set 'spec.server.customCheProperties.CHE_MULTIUSER' to 'true' in %s CheCluster custom resource. Switching to multi user authentication mode without backing up your data will cause data loss.", checluster.Name)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -36,7 +36,7 @@ import (
|
|||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
rbac "k8s.io/api/rbac/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
k8sruntime "k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/discovery"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/builder"
|
||||
|
|
@ -53,16 +53,7 @@ import (
|
|||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
// CheServiceAccountName - service account name for che-server.
|
||||
CheServiceAccountName = "che"
|
||||
)
|
||||
|
||||
const (
|
||||
failedValidationReason = "InstallOrUpdateFailed"
|
||||
failedNoOpenshiftUser = "NoOpenshiftUsers"
|
||||
failedNoIdentityProviders = "NoIdentityProviders"
|
||||
failedUnableToGetOAuth = "UnableToGetOpenshiftOAuth"
|
||||
warningNoIdentityProvidersMessage = "No Openshift identity providers."
|
||||
|
||||
AddIdentityProviderMessage = "Openshift oAuth was disabled. How to add identity provider read in the Help Link:"
|
||||
|
|
@ -76,7 +67,7 @@ const (
|
|||
// CheClusterReconciler reconciles a CheCluster object
|
||||
type CheClusterReconciler struct {
|
||||
Log logr.Logger
|
||||
Scheme *runtime.Scheme
|
||||
Scheme *k8sruntime.Scheme
|
||||
|
||||
// This client, initialized using mgr.Client() above, is a split client
|
||||
// that reads objects from the cache and writes to the apiserver
|
||||
|
|
@ -89,31 +80,41 @@ type CheClusterReconciler struct {
|
|||
nonCachedClient client.Client
|
||||
// A discovery client to check for the existence of certain APIs registered
|
||||
// in the API Server
|
||||
discoveryClient discovery.DiscoveryInterface
|
||||
tests bool
|
||||
userHandler OpenShiftOAuthUserHandler
|
||||
permissionChecker PermissionChecker
|
||||
discoveryClient discovery.DiscoveryInterface
|
||||
tests bool
|
||||
userHandler OpenShiftOAuthUserHandler
|
||||
reconcileManager *deploy.ReconcileManager
|
||||
// the namespace to which to limit the reconciliation. If empty, all namespaces are considered
|
||||
namespace string
|
||||
}
|
||||
|
||||
// NewReconciler returns a new CheClusterReconciler
|
||||
func NewReconciler(mgr ctrl.Manager, namespace string, discoveryClient *discovery.DiscoveryClient) (*CheClusterReconciler, error) {
|
||||
noncachedClient, err := client.New(mgr.GetConfig(), client.Options{Scheme: mgr.GetScheme()})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func NewReconciler(
|
||||
k8sclient client.Client,
|
||||
noncachedClient client.Client,
|
||||
discoveryClient discovery.DiscoveryInterface,
|
||||
scheme *k8sruntime.Scheme,
|
||||
namespace string) *CheClusterReconciler {
|
||||
|
||||
reconcileManager := deploy.NewReconcileManager()
|
||||
|
||||
// order does matter
|
||||
if !util.IsTestMode() {
|
||||
reconcileManager.RegisterReconciler(NewCheClusterValidator())
|
||||
}
|
||||
reconcileManager.RegisterReconciler(deploy.NewImagePuller())
|
||||
|
||||
return &CheClusterReconciler{
|
||||
Scheme: mgr.GetScheme(),
|
||||
Scheme: scheme,
|
||||
Log: ctrl.Log.WithName("controllers").WithName("CheCluster"),
|
||||
|
||||
client: mgr.GetClient(),
|
||||
nonCachedClient: noncachedClient,
|
||||
discoveryClient: discoveryClient,
|
||||
userHandler: NewOpenShiftOAuthUserHandler(noncachedClient),
|
||||
permissionChecker: &K8sApiPermissionChecker{},
|
||||
namespace: namespace,
|
||||
}, nil
|
||||
client: k8sclient,
|
||||
nonCachedClient: noncachedClient,
|
||||
discoveryClient: discoveryClient,
|
||||
userHandler: NewOpenShiftOAuthUserHandler(noncachedClient),
|
||||
namespace: namespace,
|
||||
reconcileManager: reconcileManager,
|
||||
}
|
||||
}
|
||||
|
||||
// SetupWithManager sets up the controller with the Manager.
|
||||
|
|
@ -229,15 +230,16 @@ func (r *CheClusterReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
|||
func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
_ = r.Log.WithValues("checluster", req.NamespacedName)
|
||||
|
||||
tests := r.tests
|
||||
clusterAPI := deploy.ClusterAPI{
|
||||
Client: r.client,
|
||||
NonCachedClient: r.nonCachedClient,
|
||||
DiscoveryClient: r.discoveryClient,
|
||||
Scheme: r.Scheme,
|
||||
}
|
||||
|
||||
// Fetch the CheCluster instance
|
||||
tests := r.tests
|
||||
instance, err := r.GetCR(req)
|
||||
checluster, err := r.GetCR(req)
|
||||
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
|
|
@ -252,10 +254,10 @@ func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
|||
|
||||
deployContext := &deploy.DeployContext{
|
||||
ClusterAPI: clusterAPI,
|
||||
CheCluster: instance,
|
||||
CheCluster: checluster,
|
||||
}
|
||||
|
||||
if isCheGoingToBeUpdated(instance) {
|
||||
if isCheGoingToBeUpdated(checluster) {
|
||||
// Current operator is newer than deployed Che
|
||||
backupCR, err := getBackupCRForUpdate(deployContext)
|
||||
if err != nil {
|
||||
|
|
@ -278,41 +280,33 @@ func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
|||
// Proceed anyway
|
||||
}
|
||||
|
||||
if deployContext.CheCluster.ObjectMeta.DeletionTimestamp.IsZero() {
|
||||
result, done, err := r.reconcileManager.ReconcileAll(deployContext)
|
||||
if !done {
|
||||
return result, err
|
||||
// TODO: uncomment when all items added to ReconcilerManager
|
||||
// } else {
|
||||
// logrus.Info("Successfully reconciled.")
|
||||
// return ctrl.Result{}, nil
|
||||
}
|
||||
} else {
|
||||
r.reconcileManager.FinalizeAll(deployContext)
|
||||
}
|
||||
|
||||
// Reconcile finalizers before CR is deleted
|
||||
// TODO remove in favor of r.reconcileManager.FinalizeAll(deployContext)
|
||||
r.reconcileFinalizers(deployContext)
|
||||
|
||||
// Reconcile the imagePuller section of the CheCluster
|
||||
imagePullerResult, err := deploy.ReconcileImagePuller(deployContext)
|
||||
if err != nil {
|
||||
return imagePullerResult, err
|
||||
}
|
||||
if imagePullerResult.Requeue || imagePullerResult.RequeueAfter > 0 {
|
||||
return imagePullerResult, err
|
||||
}
|
||||
|
||||
// Check Che CR correctness
|
||||
if !util.IsTestMode() {
|
||||
if err := ValidateCheCR(instance); err != nil {
|
||||
// Che cannot be deployed with current configuration.
|
||||
// Print error message in logs and wait until the configuration is changed.
|
||||
logrus.Error(err)
|
||||
if err := deploy.SetStatusDetails(deployContext, failedValidationReason, err.Error(), ""); err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
}
|
||||
|
||||
if util.IsOpenShift4 && util.IsDeleteOAuthInitialUser(instance) {
|
||||
if util.IsOpenShift4 && util.IsDeleteOAuthInitialUser(checluster) {
|
||||
if err := r.userHandler.DeleteOAuthInitialUser(deployContext); err != nil {
|
||||
logrus.Errorf("Unable to delete initial OpenShift OAuth user from a cluster. Cause: %s", err.Error())
|
||||
instance.Spec.Auth.InitialOpenShiftOAuthUser = nil
|
||||
checluster.Spec.Auth.InitialOpenShiftOAuthUser = nil
|
||||
err := deploy.UpdateCheCRSpec(deployContext, "initialOpenShiftOAuthUser", "nil")
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
|
||||
instance.Spec.Auth.OpenShiftoAuth = nil
|
||||
instance.Spec.Auth.InitialOpenShiftOAuthUser = nil
|
||||
checluster.Spec.Auth.OpenShiftoAuth = nil
|
||||
checluster.Spec.Auth.InitialOpenShiftOAuthUser = nil
|
||||
updateFields := map[string]string{
|
||||
"openShiftoAuth": "nil",
|
||||
"initialOpenShiftOAuthUser": "nil",
|
||||
|
|
@ -326,30 +320,30 @@ func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
|||
}
|
||||
|
||||
// Update status if OpenShift initial user is deleted (in the previous step)
|
||||
if instance.Spec.Auth.InitialOpenShiftOAuthUser == nil && instance.Status.OpenShiftOAuthUserCredentialsSecret != "" {
|
||||
if checluster.Spec.Auth.InitialOpenShiftOAuthUser == nil && checluster.Status.OpenShiftOAuthUserCredentialsSecret != "" {
|
||||
secret := &corev1.Secret{}
|
||||
exists, err := getOpenShiftOAuthUserCredentialsSecret(deployContext, secret)
|
||||
if err != nil {
|
||||
// We should `Requeue` since we deal with cluster scope objects
|
||||
return ctrl.Result{RequeueAfter: time.Second}, err
|
||||
} else if !exists {
|
||||
instance.Status.OpenShiftOAuthUserCredentialsSecret = ""
|
||||
checluster.Status.OpenShiftOAuthUserCredentialsSecret = ""
|
||||
if err := deploy.UpdateCheCRStatus(deployContext, "openShiftOAuthUserCredentialsSecret", ""); err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if util.IsOpenShift && instance.Spec.DevWorkspace.Enable && instance.Spec.Auth.NativeUserMode == nil {
|
||||
if util.IsOpenShift && checluster.Spec.DevWorkspace.Enable && checluster.Spec.Auth.NativeUserMode == nil {
|
||||
newNativeUserModeValue := util.NewBoolPointer(true)
|
||||
instance.Spec.Auth.NativeUserMode = newNativeUserModeValue
|
||||
checluster.Spec.Auth.NativeUserMode = newNativeUserModeValue
|
||||
if err := deploy.UpdateCheCRSpec(deployContext, "nativeUserMode", strconv.FormatBool(*newNativeUserModeValue)); err != nil {
|
||||
return reconcile.Result{Requeue: true, RequeueAfter: time.Second * 1}, err
|
||||
}
|
||||
}
|
||||
|
||||
if util.IsOpenShift && instance.Spec.Auth.OpenShiftoAuth == nil {
|
||||
if reconcileResult, err := r.autoEnableOAuth(deployContext, req, util.IsOpenShift4); err != nil {
|
||||
if util.IsOpenShift && checluster.Spec.Auth.OpenShiftoAuth == nil {
|
||||
if reconcileResult, err := r.autoEnableOAuth(deployContext); err != nil {
|
||||
return reconcileResult, err
|
||||
}
|
||||
}
|
||||
|
|
@ -376,7 +370,7 @@ func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
|||
if proxy.TrustedCAMapName != "" {
|
||||
provisioned, err := r.putOpenShiftCertsIntoConfigMap(deployContext)
|
||||
if !provisioned {
|
||||
configMapName := instance.Spec.Server.ServerTrustStoreConfigMapName
|
||||
configMapName := checluster.Spec.Server.ServerTrustStoreConfigMapName
|
||||
if err != nil {
|
||||
r.Log.Error(err, "Error on provisioning", "config map", configMapName)
|
||||
} else {
|
||||
|
|
@ -399,13 +393,13 @@ func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
|||
// To use Openshift v4 OAuth, the OAuth endpoints are served from a namespace
|
||||
// and NOT from the Openshift API Master URL (as in v3)
|
||||
// So we also need the self-signed certificate to access them (same as the Che server)
|
||||
(util.IsOpenShift4 && util.IsOAuthEnabled(instance) && !instance.Spec.Server.TlsSupport) {
|
||||
(util.IsOpenShift4 && util.IsOAuthEnabled(checluster) && !checluster.Spec.Server.TlsSupport) {
|
||||
if err := deploy.CreateTLSSecretFromEndpoint(deployContext, "", deploy.CheTLSSelfSignedCertificateSecretName); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
}
|
||||
|
||||
if util.IsOAuthEnabled(instance) {
|
||||
if util.IsOAuthEnabled(checluster) {
|
||||
// create a secret with OpenShift API crt to be added to keystore that RH SSO will consume
|
||||
apiUrl, apiInternalUrl, err := util.GetOpenShiftAPIUrls()
|
||||
if err != nil {
|
||||
|
|
@ -419,8 +413,8 @@ func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
|||
}
|
||||
} else {
|
||||
// Handle Che TLS certificates on Kubernetes infrastructure
|
||||
if instance.Spec.Server.TlsSupport {
|
||||
if instance.Spec.K8s.TlsSecretName != "" {
|
||||
if checluster.Spec.Server.TlsSupport {
|
||||
if checluster.Spec.K8s.TlsSecretName != "" {
|
||||
// Self-signed certificate should be created to secure Che ingresses
|
||||
result, err := deploy.K8sHandleCheTLSSecrets(deployContext)
|
||||
if result.Requeue || result.RequeueAfter > 0 {
|
||||
|
|
@ -452,14 +446,10 @@ func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
|||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
if err := deploy.SetStatusDetails(deployContext, "", "", ""); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
// Create service account "che" for che-server component.
|
||||
// "che" is the one which token is used to create workspace objects.
|
||||
// Notice: Also we have on more "che-workspace" SA used by plugins like exec, terminal, metrics with limited privileges.
|
||||
done, err = deploy.SyncServiceAccountToCluster(deployContext, CheServiceAccountName)
|
||||
done, err = deploy.SyncServiceAccountToCluster(deployContext, deploy.CheServiceAccountName)
|
||||
if !done {
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
|
|
@ -484,12 +474,12 @@ func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
|||
return ctrl.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
|
||||
if len(instance.Spec.Server.CheClusterRoles) > 0 {
|
||||
cheClusterRoles := strings.Split(instance.Spec.Server.CheClusterRoles, ",")
|
||||
if len(checluster.Spec.Server.CheClusterRoles) > 0 {
|
||||
cheClusterRoles := strings.Split(checluster.Spec.Server.CheClusterRoles, ",")
|
||||
for _, cheClusterRole := range cheClusterRoles {
|
||||
cheClusterRole := strings.TrimSpace(cheClusterRole)
|
||||
cheClusterRoleBindingName := cheClusterRole
|
||||
done, err := deploy.SyncClusterRoleBindingAndAddFinalizerToCluster(deployContext, cheClusterRoleBindingName, CheServiceAccountName, cheClusterRole)
|
||||
done, err := deploy.SyncClusterRoleBindingAndAddFinalizerToCluster(deployContext, cheClusterRoleBindingName, deploy.CheServiceAccountName, cheClusterRole)
|
||||
if !tests {
|
||||
if !done {
|
||||
logrus.Infof("Waiting on cluster role binding '%s' to be created", cheClusterRoleBindingName)
|
||||
|
|
@ -504,7 +494,7 @@ func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
|||
|
||||
// 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
|
||||
workspaceClusterRole := checluster.Spec.Server.CheWorkspaceClusterRole
|
||||
if workspaceClusterRole != "" {
|
||||
done, err := deploy.SyncRoleBindingToCluster(deployContext, "che-workspace-custom", "view", workspaceClusterRole, "ClusterRole")
|
||||
if !done {
|
||||
|
|
@ -515,8 +505,8 @@ func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
|||
}
|
||||
}
|
||||
|
||||
if err := r.GenerateAndSaveFields(deployContext, req); err != nil {
|
||||
instance, _ = r.GetCR(req)
|
||||
if err := r.GenerateAndSaveFields(deployContext); err != nil {
|
||||
_ = deploy.ReloadCheClusterCR(deployContext)
|
||||
return ctrl.Result{Requeue: true, RequeueAfter: time.Second * 1}, err
|
||||
}
|
||||
|
||||
|
|
@ -543,7 +533,7 @@ func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
|||
}
|
||||
|
||||
// create and provision Keycloak related objects
|
||||
if !instance.Spec.Auth.ExternalIdentityProvider {
|
||||
if !checluster.Spec.Auth.ExternalIdentityProvider {
|
||||
provisioned, err := identity_provider.SyncIdentityProviderToCluster(deployContext)
|
||||
if !provisioned {
|
||||
if err != nil {
|
||||
|
|
@ -552,9 +542,9 @@ func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
|||
return ctrl.Result{}, err
|
||||
}
|
||||
} else {
|
||||
keycloakURL := instance.Spec.Auth.IdentityProviderURL
|
||||
if instance.Status.KeycloakURL != keycloakURL {
|
||||
instance.Status.KeycloakURL = keycloakURL
|
||||
keycloakURL := checluster.Spec.Auth.IdentityProviderURL
|
||||
if checluster.Status.KeycloakURL != keycloakURL {
|
||||
checluster.Status.KeycloakURL = keycloakURL
|
||||
if err := deploy.UpdateCheCRStatus(deployContext, "status: Keycloak URL", keycloakURL); err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
|
|
@ -562,7 +552,7 @@ func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
|||
}
|
||||
|
||||
devfileRegistry := devfileregistry.NewDevfileRegistry(deployContext)
|
||||
if !instance.Spec.Server.ExternalDevfileRegistry {
|
||||
if !checluster.Spec.Server.ExternalDevfileRegistry {
|
||||
done, err := devfileRegistry.SyncAll()
|
||||
if !done {
|
||||
if err != nil {
|
||||
|
|
@ -572,7 +562,7 @@ func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
|||
}
|
||||
}
|
||||
|
||||
if !instance.Spec.Server.ExternalPluginRegistry {
|
||||
if !checluster.Spec.Server.ExternalPluginRegistry {
|
||||
pluginRegistry := pluginregistry.NewPluginRegistry(deployContext)
|
||||
done, err := pluginRegistry.SyncAll()
|
||||
if !done {
|
||||
|
|
@ -582,9 +572,9 @@ func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
|||
return ctrl.Result{}, err
|
||||
}
|
||||
} else {
|
||||
if instance.Spec.Server.PluginRegistryUrl != instance.Status.PluginRegistryURL {
|
||||
instance.Status.PluginRegistryURL = instance.Spec.Server.PluginRegistryUrl
|
||||
if err := deploy.UpdateCheCRStatus(deployContext, "status: Plugin Registry URL", instance.Spec.Server.PluginRegistryUrl); err != nil {
|
||||
if checluster.Spec.Server.PluginRegistryUrl != checluster.Status.PluginRegistryURL {
|
||||
checluster.Status.PluginRegistryURL = checluster.Spec.Server.PluginRegistryUrl
|
||||
if err := deploy.UpdateCheCRStatus(deployContext, "status: Plugin Registry URL", checluster.Spec.Server.PluginRegistryUrl); err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
}
|
||||
|
|
@ -630,39 +620,37 @@ func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
|||
// ignore error
|
||||
deploy.DeleteFinalizer(deployContext, deploy.OAuthFinalizerName)
|
||||
for {
|
||||
instance.Status.OpenShiftoAuthProvisioned = false
|
||||
checluster.Status.OpenShiftoAuthProvisioned = false
|
||||
if err := deploy.UpdateCheCRStatus(deployContext, "status: provisioned with OpenShift identity provider", "false"); err != nil &&
|
||||
errors.IsConflict(err) {
|
||||
instance, _ = r.GetCR(req)
|
||||
_ = deploy.ReloadCheClusterCR(deployContext)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
for {
|
||||
instance.Spec.Auth.OAuthSecret = ""
|
||||
instance.Spec.Auth.OAuthClientName = ""
|
||||
checluster.Spec.Auth.OAuthSecret = ""
|
||||
checluster.Spec.Auth.OAuthClientName = ""
|
||||
if err := deploy.UpdateCheCRStatus(deployContext, "clean oAuth secret name and client name", ""); err != nil &&
|
||||
errors.IsConflict(err) {
|
||||
instance, _ = r.GetCR(req)
|
||||
_ = deploy.ReloadCheClusterCR(deployContext)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
logrus.Info("Successfully reconciled.")
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
func (r *CheClusterReconciler) autoEnableOAuth(deployContext *deploy.DeployContext, request ctrl.Request, isOpenShift4 bool) (reconcile.Result, error) {
|
||||
var message, reason string
|
||||
func (r *CheClusterReconciler) autoEnableOAuth(deployContext *deploy.DeployContext) (reconcile.Result, error) {
|
||||
oauth := false
|
||||
cr := deployContext.CheCluster
|
||||
if isOpenShift4 {
|
||||
if util.IsOpenShift4 {
|
||||
openshitOAuth, err := GetOpenshiftOAuth(deployContext.ClusterAPI.NonCachedClient)
|
||||
if err != nil {
|
||||
message = "Unable to get Openshift oAuth. Cause: " + err.Error()
|
||||
logrus.Error(message)
|
||||
reason = failedUnableToGetOAuth
|
||||
logrus.Error("Unable to get Openshift oAuth. Cause: " + err.Error())
|
||||
} else {
|
||||
if len(openshitOAuth.Spec.IdentityProviders) > 0 {
|
||||
oauth = true
|
||||
|
|
@ -673,10 +661,8 @@ func (r *CheClusterReconciler) autoEnableOAuth(deployContext *deploy.DeployConte
|
|||
} else if util.IsInitialOpenShiftOAuthUserEnabled(cr) {
|
||||
provisioned, err := r.userHandler.SyncOAuthInitialUser(openshitOAuth, deployContext)
|
||||
if err != nil {
|
||||
message = warningNoIdentityProvidersMessage + " Operator tried to create initial OpenShift OAuth user for HTPasswd identity provider, but failed. Cause: " + err.Error()
|
||||
logrus.Error(message)
|
||||
logrus.Error(warningNoIdentityProvidersMessage + " Operator tried to create initial OpenShift OAuth user for HTPasswd identity provider, but failed. Cause: " + err.Error())
|
||||
logrus.Info("To enable OpenShift OAuth, please add identity provider first: " + howToAddIdentityProviderLinkOS4)
|
||||
reason = failedNoIdentityProviders
|
||||
// Don't try to create initial user any more, che-operator shouldn't hang on this step.
|
||||
cr.Spec.Auth.InitialOpenShiftOAuthUser = nil
|
||||
if err := deploy.UpdateCheCRStatus(deployContext, "initialOpenShiftOAuthUser", ""); err != nil {
|
||||
|
|
@ -701,15 +687,11 @@ func (r *CheClusterReconciler) autoEnableOAuth(deployContext *deploy.DeployConte
|
|||
users := &userv1.UserList{}
|
||||
listOptions := &client.ListOptions{}
|
||||
if err := r.nonCachedClient.List(context.TODO(), users, listOptions); err != nil {
|
||||
message = failedUnableToGetOpenshiftUsers + " Cause: " + err.Error()
|
||||
logrus.Error(message)
|
||||
reason = failedNoOpenshiftUser
|
||||
logrus.Error(failedUnableToGetOpenshiftUsers + " Cause: " + err.Error())
|
||||
} else {
|
||||
oauth = len(users.Items) >= 1
|
||||
if !oauth {
|
||||
message = warningNoRealUsersMessage + " " + howToConfigureOAuthLinkOS3
|
||||
logrus.Warn(message)
|
||||
reason = failedNoOpenshiftUser
|
||||
logrus.Warn(warningNoRealUsersMessage + " " + howToConfigureOAuthLinkOS3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -722,12 +704,6 @@ func (r *CheClusterReconciler) autoEnableOAuth(deployContext *deploy.DeployConte
|
|||
}
|
||||
}
|
||||
|
||||
if message != "" && reason != "" {
|
||||
if err := deploy.SetStatusDetails(deployContext, message, reason, ""); err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
}
|
||||
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
|
||||
|
|
@ -775,7 +751,7 @@ func (r *CheClusterReconciler) reconcileFinalizers(deployContext *deploy.DeployC
|
|||
}
|
||||
|
||||
// Removes any legacy CRB https://github.com/eclipse/che/issues/19506
|
||||
cheClusterRoleBindingName = deploy.GetLegacyUniqueClusterRoleBindingName(deployContext, CheServiceAccountName, cheClusterRole)
|
||||
cheClusterRoleBindingName = deploy.GetLegacyUniqueClusterRoleBindingName(deployContext, deploy.CheServiceAccountName, cheClusterRole)
|
||||
if err := deploy.ReconcileLegacyClusterRoleBindingFinalizer(deployContext, cheClusterRoleBindingName); err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,6 @@ import (
|
|||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
mocks "github.com/eclipse-che/che-operator/mocks"
|
||||
|
||||
|
|
@ -30,7 +28,6 @@ import (
|
|||
|
||||
identity_provider "github.com/eclipse-che/che-operator/pkg/deploy/identity-provider"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
|
||||
"github.com/eclipse-che/che-operator/pkg/deploy"
|
||||
"github.com/eclipse-che/che-operator/pkg/util"
|
||||
|
|
@ -59,7 +56,6 @@ import (
|
|||
fakeDiscovery "k8s.io/client-go/discovery/fake"
|
||||
fakeclientset "k8s.io/client-go/kubernetes/fake"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
|
||||
|
|
@ -73,51 +69,7 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
namespace = "eclipse-che"
|
||||
csvName = "kubernetes-imagepuller-operator.v0.0.9"
|
||||
packageManifest = &packagesv1.PackageManifest{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "kubernetes-imagepuller-operator",
|
||||
Namespace: namespace,
|
||||
},
|
||||
Status: packagesv1.PackageManifestStatus{
|
||||
CatalogSource: "community-operators",
|
||||
CatalogSourceNamespace: "olm",
|
||||
DefaultChannel: "stable",
|
||||
PackageName: "kubernetes-imagepuller-operator",
|
||||
},
|
||||
}
|
||||
operatorGroup = &operatorsv1.OperatorGroup{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "kubernetes-imagepuller-operator",
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: operatorsv1.OperatorGroupSpec{
|
||||
TargetNamespaces: []string{
|
||||
namespace,
|
||||
},
|
||||
},
|
||||
}
|
||||
subscription = &operatorsv1alpha1.Subscription{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "kubernetes-imagepuller-operator",
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: &operatorsv1alpha1.SubscriptionSpec{
|
||||
CatalogSource: "community-operators",
|
||||
Channel: "stable",
|
||||
CatalogSourceNamespace: "olm",
|
||||
InstallPlanApproval: operatorsv1alpha1.ApprovalAutomatic,
|
||||
Package: "kubernetes-imagepuller-operator",
|
||||
},
|
||||
}
|
||||
valueTrue = true
|
||||
clusterServiceVersion = &operatorsv1alpha1.ClusterServiceVersion{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: namespace,
|
||||
Name: csvName,
|
||||
},
|
||||
}
|
||||
namespace = "eclipse-che"
|
||||
nonEmptyUserList = &userv1.UserList{
|
||||
Items: []userv1.User{
|
||||
{
|
||||
|
|
@ -158,7 +110,6 @@ var (
|
|||
Name: "cluster",
|
||||
},
|
||||
}
|
||||
defaultImagePullerImages string
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
@ -170,8 +121,6 @@ func init() {
|
|||
os.Setenv(env.Name, env.Value)
|
||||
}
|
||||
}
|
||||
defaultImagePullerImages = "che-workspace-plugin-broker-metadata=" + os.Getenv("RELATED_IMAGE_che_workspace_plugin_broker_metadata") +
|
||||
";che-workspace-plugin-broker-artifacts=" + os.Getenv("RELATED_IMAGE_che_workspace_plugin_broker_artifacts") + ";"
|
||||
}
|
||||
|
||||
func TestNativeUserModeEnabled(t *testing.T) {
|
||||
|
|
@ -249,14 +198,9 @@ func TestNativeUserModeEnabled(t *testing.T) {
|
|||
t.Fatal("Error creating fake discovery client")
|
||||
}
|
||||
|
||||
r := &CheClusterReconciler{
|
||||
client: cli,
|
||||
nonCachedClient: nonCachedClient,
|
||||
discoveryClient: fakeDiscovery,
|
||||
Scheme: scheme,
|
||||
tests: true,
|
||||
Log: ctrl.Log.WithName("controllers").WithName("CheCluster"),
|
||||
}
|
||||
r := NewReconciler(cli, nonCachedClient, fakeDiscovery, scheme, "")
|
||||
r.tests = true
|
||||
|
||||
req := reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{
|
||||
Name: os.Getenv("CHE_FLAVOR"),
|
||||
|
|
@ -468,15 +412,10 @@ func TestCaseAutoDetectOAuth(t *testing.T) {
|
|||
defer ctrl.Finish()
|
||||
}
|
||||
|
||||
r := &CheClusterReconciler{
|
||||
client: cli,
|
||||
nonCachedClient: nonCachedClient,
|
||||
discoveryClient: fakeDiscovery,
|
||||
Scheme: scheme,
|
||||
tests: true,
|
||||
userHandler: userHandlerMock,
|
||||
Log: ctrl.Log.WithName("controllers").WithName("CheCluster"),
|
||||
}
|
||||
r := NewReconciler(cli, nonCachedClient, fakeDiscovery, scheme, "")
|
||||
r.userHandler = userHandlerMock
|
||||
r.tests = true
|
||||
|
||||
req := reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{
|
||||
Name: os.Getenv("CHE_FLAVOR"),
|
||||
|
|
@ -566,14 +505,9 @@ func TestEnsureServerExposureStrategy(t *testing.T) {
|
|||
t.Fatal("Error creating fake discovery client")
|
||||
}
|
||||
|
||||
r := &CheClusterReconciler{
|
||||
client: cli,
|
||||
nonCachedClient: nonCachedClient,
|
||||
discoveryClient: fakeDiscovery,
|
||||
Scheme: scheme,
|
||||
tests: true,
|
||||
Log: ctrl.Log.WithName("controllers").WithName("CheCluster"),
|
||||
}
|
||||
r := NewReconciler(cli, nonCachedClient, fakeDiscovery, scheme, "")
|
||||
r.tests = true
|
||||
|
||||
req := reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{
|
||||
Name: os.Getenv("CHE_FLAVOR"),
|
||||
|
|
@ -690,14 +624,9 @@ func TestShouldSetUpCorrectlyDevfileRegistryURL(t *testing.T) {
|
|||
}
|
||||
fakeDiscovery.Fake.Resources = []*metav1.APIResourceList{}
|
||||
|
||||
r := &CheClusterReconciler{
|
||||
client: cli,
|
||||
nonCachedClient: nonCachedClient,
|
||||
discoveryClient: fakeDiscovery,
|
||||
Scheme: scheme,
|
||||
tests: true,
|
||||
Log: ctrl.Log.WithName("controllers").WithName("CheCluster"),
|
||||
}
|
||||
r := NewReconciler(cli, nonCachedClient, fakeDiscovery, scheme, "")
|
||||
r.tests = true
|
||||
|
||||
req := reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{
|
||||
Name: os.Getenv("CHE_FLAVOR"),
|
||||
|
|
@ -725,376 +654,6 @@ func TestShouldSetUpCorrectlyDevfileRegistryURL(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestImagePullerConfiguration(t *testing.T) {
|
||||
oldBrokerMetaDataImage := strings.Split(os.Getenv("RELATED_IMAGE_che_workspace_plugin_broker_metadata"), ":")[0] + ":old"
|
||||
oldBrokerArtifactsImage := strings.Split(os.Getenv("RELATED_IMAGE_che_workspace_plugin_broker_artifacts"), ":")[0] + ":old"
|
||||
type testCase struct {
|
||||
name string
|
||||
initCR *orgv1.CheCluster
|
||||
initObjects []runtime.Object
|
||||
expectedCR *orgv1.CheCluster
|
||||
expectedOperatorGroup *operatorsv1.OperatorGroup
|
||||
expectedSubscription *operatorsv1alpha1.Subscription
|
||||
expectedImagePuller *chev1alpha1.KubernetesImagePuller
|
||||
shouldDelete bool
|
||||
}
|
||||
|
||||
testCases := []testCase{
|
||||
{
|
||||
name: "image puller enabled, no operatorgroup, should create an operatorgroup",
|
||||
initCR: InitCheCRWithImagePullerEnabled(),
|
||||
initObjects: []runtime.Object{
|
||||
packageManifest,
|
||||
},
|
||||
expectedOperatorGroup: operatorGroup,
|
||||
},
|
||||
{
|
||||
name: "image puller enabled, operatorgroup exists, should create a subscription",
|
||||
initCR: InitCheCRWithImagePullerEnabled(),
|
||||
initObjects: []runtime.Object{
|
||||
packageManifest,
|
||||
operatorGroup,
|
||||
},
|
||||
expectedSubscription: subscription,
|
||||
},
|
||||
{
|
||||
name: "image puller enabled, subscription created, should add finalizer",
|
||||
initCR: InitCheCRWithImagePullerEnabled(),
|
||||
expectedCR: ExpectedCheCRWithImagePullerFinalizer(),
|
||||
initObjects: []runtime.Object{
|
||||
packageManifest,
|
||||
operatorGroup,
|
||||
subscription,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "image puller enabled with finalizer but default values are empty, subscription exists, should update the CR",
|
||||
initCR: InitCheCRWithImagePullerFinalizer(),
|
||||
expectedCR: &orgv1.CheCluster{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "CheCluster",
|
||||
APIVersion: "org.eclipse.che/v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: os.Getenv("CHE_FLAVOR"),
|
||||
Namespace: namespace,
|
||||
ResourceVersion: "2",
|
||||
Finalizers: []string{
|
||||
"kubernetesimagepullers.finalizers.che.eclipse.org",
|
||||
},
|
||||
},
|
||||
Spec: orgv1.CheClusterSpec{
|
||||
ImagePuller: orgv1.CheClusterSpecImagePuller{
|
||||
Enable: true,
|
||||
Spec: chev1alpha1.KubernetesImagePullerSpec{
|
||||
DeploymentName: "kubernetes-image-puller",
|
||||
ConfigMapName: "k8s-image-puller",
|
||||
},
|
||||
},
|
||||
Server: orgv1.CheClusterSpecServer{
|
||||
ServerExposureStrategy: "multi-host",
|
||||
},
|
||||
},
|
||||
},
|
||||
initObjects: []runtime.Object{
|
||||
packageManifest,
|
||||
operatorGroup,
|
||||
subscription,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "image puller enabled default values already set, subscription exists, should create a KubernetesImagePuller",
|
||||
initCR: InitCheCRWithImagePullerEnabledAndDefaultValuesSet(),
|
||||
initObjects: []runtime.Object{
|
||||
packageManifest,
|
||||
operatorGroup,
|
||||
subscription,
|
||||
},
|
||||
expectedImagePuller: InitImagePuller(ImagePullerOptions{SpecImages: defaultImagePullerImages, ObjectMetaResourceVersion: "1"}),
|
||||
},
|
||||
{
|
||||
name: "image puller enabled, user images set, subscription exists, should create a KubernetesImagePuller with user images",
|
||||
initCR: InitCheCRWithImagePullerEnabledAndImagesSet("image=image_url"),
|
||||
initObjects: []runtime.Object{
|
||||
packageManifest,
|
||||
operatorGroup,
|
||||
subscription,
|
||||
},
|
||||
expectedImagePuller: InitImagePuller(ImagePullerOptions{SpecImages: "image=image_url", ObjectMetaResourceVersion: "1"}),
|
||||
},
|
||||
{
|
||||
name: "image puller enabled, one default image set, subscription exists, should update KubernetesImagePuller default image",
|
||||
initCR: InitCheCRWithImagePullerEnabledAndImagesSet("che-workspace-plugin-broker-metadata=" + oldBrokerMetaDataImage + ";"),
|
||||
initObjects: []runtime.Object{
|
||||
packageManifest,
|
||||
operatorGroup,
|
||||
subscription,
|
||||
InitImagePuller(ImagePullerOptions{SpecImages: "che-workspace-plugin-broker-metadata=" + oldBrokerMetaDataImage + ";", ObjectMetaResourceVersion: "1"}),
|
||||
},
|
||||
expectedImagePuller: InitImagePuller(ImagePullerOptions{SpecImages: "che-workspace-plugin-broker-metadata=" + os.Getenv("RELATED_IMAGE_che_workspace_plugin_broker_metadata") + ";", ObjectMetaResourceVersion: "2"}),
|
||||
},
|
||||
{
|
||||
name: "image puller enabled, one default image set, subscription exists, should update KubernetesImagePuller default images while keeping user image",
|
||||
initCR: InitCheCRWithImagePullerEnabledAndImagesSet("image=image_url;che-workspace-plugin-broker-metadata=" + oldBrokerMetaDataImage + ";"),
|
||||
initObjects: []runtime.Object{
|
||||
packageManifest,
|
||||
operatorGroup,
|
||||
subscription,
|
||||
InitImagePuller(ImagePullerOptions{SpecImages: "image=image_url;che-workspace-plugin-broker-metadata=" + oldBrokerMetaDataImage + ";", ObjectMetaResourceVersion: "1"}),
|
||||
},
|
||||
expectedImagePuller: InitImagePuller(ImagePullerOptions{SpecImages: "image=image_url;che-workspace-plugin-broker-metadata=" + os.Getenv("RELATED_IMAGE_che_workspace_plugin_broker_metadata") + ";", ObjectMetaResourceVersion: "2"}),
|
||||
},
|
||||
{
|
||||
name: "image puller enabled, default images set, subscription exists, should update KubernetesImagePuller default images",
|
||||
initCR: InitCheCRWithImagePullerEnabledAndImagesSet("che-workspace-plugin-broker-metadata=" + oldBrokerMetaDataImage + ";che-workspace-plugin-broker-artifacts=" + oldBrokerArtifactsImage + ";"),
|
||||
initObjects: []runtime.Object{
|
||||
packageManifest,
|
||||
operatorGroup,
|
||||
subscription,
|
||||
InitImagePuller(ImagePullerOptions{SpecImages: "che-workspace-plugin-broker-metadata=" + oldBrokerMetaDataImage + ";che-workspace-plugin-broker-artifacts=" + oldBrokerArtifactsImage + ";", ObjectMetaResourceVersion: "1"}),
|
||||
},
|
||||
expectedImagePuller: InitImagePuller(ImagePullerOptions{SpecImages: defaultImagePullerImages, ObjectMetaResourceVersion: "2"}),
|
||||
},
|
||||
{
|
||||
name: "image puller enabled, latest default images set, subscription exists, should not update KubernetesImagePuller default images",
|
||||
initCR: InitCheCRWithImagePullerEnabledAndImagesSet(defaultImagePullerImages),
|
||||
initObjects: []runtime.Object{
|
||||
packageManifest,
|
||||
operatorGroup,
|
||||
subscription,
|
||||
InitImagePuller(ImagePullerOptions{SpecImages: defaultImagePullerImages, ObjectMetaResourceVersion: "1"}),
|
||||
},
|
||||
expectedImagePuller: InitImagePuller(ImagePullerOptions{SpecImages: defaultImagePullerImages, ObjectMetaResourceVersion: "1"}),
|
||||
},
|
||||
{
|
||||
name: "image puller enabled, default images not set, subscription exists, should not set KubernetesImagePuller default images",
|
||||
initCR: InitCheCRWithImagePullerEnabledAndImagesSet("image=image_url;"),
|
||||
initObjects: []runtime.Object{
|
||||
packageManifest,
|
||||
operatorGroup,
|
||||
subscription,
|
||||
InitImagePuller(ImagePullerOptions{SpecImages: "image=image_url;", ObjectMetaResourceVersion: "1"}),
|
||||
},
|
||||
expectedImagePuller: InitImagePuller(ImagePullerOptions{SpecImages: "image=image_url;", ObjectMetaResourceVersion: "1"}),
|
||||
},
|
||||
{
|
||||
name: "image puller enabled, KubernetesImagePuller created and spec in CheCluster is different, should update the KubernetesImagePuller",
|
||||
initCR: InitCheCRWithImagePullerEnabledAndNewValuesSet(),
|
||||
initObjects: []runtime.Object{
|
||||
packageManifest,
|
||||
operatorGroup,
|
||||
subscription,
|
||||
getDefaultImagePuller(),
|
||||
},
|
||||
expectedImagePuller: &chev1alpha1.KubernetesImagePuller{
|
||||
TypeMeta: metav1.TypeMeta{Kind: "KubernetesImagePuller", APIVersion: "che.eclipse.org/v1alpha1"},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
ResourceVersion: "2",
|
||||
Name: os.Getenv("CHE_FLAVOR") + "-image-puller",
|
||||
Namespace: namespace,
|
||||
Labels: map[string]string{
|
||||
"app": "che",
|
||||
"component": "kubernetes-image-puller",
|
||||
"app.kubernetes.io/part-of": os.Getenv("CHE_FLAVOR"),
|
||||
},
|
||||
OwnerReferences: []metav1.OwnerReference{
|
||||
{
|
||||
APIVersion: "org.eclipse.che/v1",
|
||||
Kind: "CheCluster",
|
||||
BlockOwnerDeletion: &valueTrue,
|
||||
Controller: &valueTrue,
|
||||
Name: os.Getenv("CHE_FLAVOR"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Spec: chev1alpha1.KubernetesImagePullerSpec{
|
||||
ConfigMapName: "k8s-image-puller-trigger-update",
|
||||
DeploymentName: "kubernetes-image-puller-trigger-update",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "image puller already created, imagePuller disabled, should delete everything",
|
||||
initCR: InitCheCRWithImagePullerDisabled(),
|
||||
initObjects: []runtime.Object{
|
||||
packageManifest,
|
||||
operatorGroup,
|
||||
subscription,
|
||||
clusterServiceVersion,
|
||||
getDefaultImagePuller(),
|
||||
},
|
||||
expectedCR: InitCheCRWithImagePullerDisabled(),
|
||||
shouldDelete: true,
|
||||
},
|
||||
{
|
||||
name: "image puller already created, finalizer deleted",
|
||||
initCR: InitCheCRWithImagePullerFinalizerAndDeletionTimestamp(),
|
||||
initObjects: []runtime.Object{
|
||||
packageManifest,
|
||||
operatorGroup,
|
||||
subscription,
|
||||
clusterServiceVersion,
|
||||
getDefaultImagePuller(),
|
||||
},
|
||||
shouldDelete: true,
|
||||
expectedCR: nil,
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
packagesv1.AddToScheme(scheme.Scheme)
|
||||
operatorsv1alpha1.AddToScheme(scheme.Scheme)
|
||||
operatorsv1.AddToScheme(scheme.Scheme)
|
||||
chev1alpha1.AddToScheme(scheme.Scheme)
|
||||
routev1.AddToScheme(scheme.Scheme)
|
||||
testCase.initObjects = append(testCase.initObjects, testCase.initCR)
|
||||
cli := fake.NewFakeClientWithScheme(scheme.Scheme, testCase.initObjects...)
|
||||
nonCachedClient := fake.NewFakeClientWithScheme(scheme.Scheme, testCase.initObjects...)
|
||||
clientSet := fakeclientset.NewSimpleClientset()
|
||||
fakeDiscovery, ok := clientSet.Discovery().(*fakeDiscovery.FakeDiscovery)
|
||||
fakeDiscovery.Fake.Resources = []*metav1.APIResourceList{
|
||||
{
|
||||
GroupVersion: "packages.operators.coreos.com/v1",
|
||||
APIResources: []metav1.APIResource{
|
||||
{
|
||||
Kind: "PackageManifest",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
GroupVersion: "operators.coreos.com/v1alpha1",
|
||||
APIResources: []metav1.APIResource{
|
||||
{Kind: "OperatorGroup"},
|
||||
{Kind: "Subscription"},
|
||||
{Kind: "ClusterServiceVersion"},
|
||||
},
|
||||
},
|
||||
{
|
||||
GroupVersion: "che.eclipse.org/v1alpha1",
|
||||
APIResources: []metav1.APIResource{
|
||||
{Kind: "KubernetesImagePuller"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if !ok {
|
||||
t.Error("Error creating fake discovery client")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
r := &CheClusterReconciler{
|
||||
client: cli,
|
||||
nonCachedClient: nonCachedClient,
|
||||
discoveryClient: fakeDiscovery,
|
||||
Scheme: scheme.Scheme,
|
||||
tests: true,
|
||||
Log: ctrl.Log.WithName("controllers").WithName("CheCluster"),
|
||||
}
|
||||
req := reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{
|
||||
Name: os.Getenv("CHE_FLAVOR"),
|
||||
Namespace: namespace,
|
||||
},
|
||||
}
|
||||
_, err := r.Reconcile(context.TODO(), req)
|
||||
if err != nil {
|
||||
t.Fatalf("Error reconciling: %v", err)
|
||||
}
|
||||
|
||||
if testCase.expectedOperatorGroup != nil {
|
||||
gotOperatorGroup := &operatorsv1.OperatorGroup{}
|
||||
err := r.nonCachedClient.Get(context.TODO(), types.NamespacedName{Namespace: testCase.expectedOperatorGroup.Namespace, Name: testCase.expectedOperatorGroup.Name}, gotOperatorGroup)
|
||||
if err != nil {
|
||||
t.Errorf("Error getting OperatorGroup: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(testCase.expectedOperatorGroup.Spec.TargetNamespaces, gotOperatorGroup.Spec.TargetNamespaces) {
|
||||
t.Errorf("Error expected target namespace %v but got %v", testCase.expectedOperatorGroup.Spec.TargetNamespaces, gotOperatorGroup.Spec.TargetNamespaces)
|
||||
}
|
||||
}
|
||||
if testCase.expectedSubscription != nil {
|
||||
gotSubscription := &operatorsv1alpha1.Subscription{}
|
||||
err := r.nonCachedClient.Get(context.TODO(), types.NamespacedName{Namespace: testCase.expectedSubscription.Namespace, Name: testCase.expectedSubscription.Name}, gotSubscription)
|
||||
if err != nil {
|
||||
t.Errorf("Error getting Subscription: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(testCase.expectedSubscription.Spec, gotSubscription.Spec) {
|
||||
t.Errorf("Error, subscriptions differ (-want +got) %v", cmp.Diff(testCase.expectedSubscription.Spec, gotSubscription.Spec))
|
||||
}
|
||||
}
|
||||
// if expectedCR is not set, don't check it
|
||||
if testCase.expectedCR != nil && !reflect.DeepEqual(testCase.initCR, testCase.expectedCR) {
|
||||
gotCR := &orgv1.CheCluster{}
|
||||
err = r.client.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: os.Getenv("CHE_FLAVOR")}, gotCR)
|
||||
if err != nil {
|
||||
t.Errorf("Error getting CheCluster: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(testCase.expectedCR, gotCR) {
|
||||
t.Errorf("Expected CR and CR returned from API server are different (-want +got): %v", cmp.Diff(testCase.expectedCR, gotCR))
|
||||
}
|
||||
}
|
||||
if testCase.expectedImagePuller != nil {
|
||||
gotImagePuller := &chev1alpha1.KubernetesImagePuller{}
|
||||
err = r.client.Get(context.TODO(), types.NamespacedName{Namespace: testCase.expectedImagePuller.Namespace, Name: testCase.expectedImagePuller.Name}, gotImagePuller)
|
||||
if err != nil {
|
||||
t.Errorf("Error getting KubernetesImagePuller: %v", err)
|
||||
}
|
||||
|
||||
diff := cmp.Diff(testCase.expectedImagePuller, gotImagePuller, cmpopts.IgnoreFields(chev1alpha1.KubernetesImagePullerSpec{}, "Images"))
|
||||
if diff != "" {
|
||||
t.Errorf("Expected KubernetesImagePuller and KubernetesImagePuller returned from API server differ (-want, +got): %v", diff)
|
||||
}
|
||||
|
||||
expectedImages := nonEmptySplit(testCase.expectedImagePuller.Spec.Images, ";")
|
||||
if len(nonEmptySplit(testCase.expectedImagePuller.Spec.Images, ";")) != len(expectedImages) {
|
||||
t.Errorf("Expected KubernetesImagePuller returns %d images", len(expectedImages))
|
||||
}
|
||||
|
||||
for _, expectedImage := range expectedImages {
|
||||
if !strings.Contains(gotImagePuller.Spec.Images, expectedImage) {
|
||||
t.Errorf("Expected KubernetesImagePuller returned image: %s, but it did not", expectedImage)
|
||||
}
|
||||
}
|
||||
}
|
||||
if testCase.shouldDelete {
|
||||
if testCase.expectedCR == nil {
|
||||
gotCR := &orgv1.CheCluster{}
|
||||
err = r.client.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: os.Getenv("CHE_FLAVOR")}, gotCR)
|
||||
if !errors.IsNotFound(err) {
|
||||
t.Fatal("CR CheCluster should be removed")
|
||||
}
|
||||
}
|
||||
|
||||
imagePuller := &chev1alpha1.KubernetesImagePuller{}
|
||||
err = r.client.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: os.Getenv("CHE_FLAVOR") + "-image-puller"}, imagePuller)
|
||||
if err == nil || !errors.IsNotFound(err) {
|
||||
t.Fatalf("Should not have found KubernetesImagePuller: %v", err)
|
||||
}
|
||||
|
||||
clusterServiceVersion := &operatorsv1alpha1.ClusterServiceVersion{}
|
||||
err = r.nonCachedClient.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: csvName}, clusterServiceVersion)
|
||||
if err == nil || !errors.IsNotFound(err) {
|
||||
t.Fatalf("Should not have found ClusterServiceVersion: %v", err)
|
||||
}
|
||||
|
||||
subscription := &operatorsv1alpha1.Subscription{}
|
||||
err = r.nonCachedClient.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: "kubernetes-imagepuller-operator"}, subscription)
|
||||
if err == nil || !errors.IsNotFound(err) {
|
||||
t.Fatalf("Should not have found Subscription: %v", err)
|
||||
}
|
||||
|
||||
operatorGroup := &operatorsv1.OperatorGroup{}
|
||||
err = r.nonCachedClient.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: "kubernetes-imagepuller-operator"}, operatorGroup)
|
||||
if err == nil || !errors.IsNotFound(err) {
|
||||
t.Fatalf("Should not have found OperatorGroup: %v", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheController(t *testing.T) {
|
||||
var err error
|
||||
|
||||
|
|
@ -1104,7 +663,8 @@ func TestCheController(t *testing.T) {
|
|||
cl, dc, scheme := Init()
|
||||
|
||||
// Create a ReconcileChe object with the scheme and fake client
|
||||
r := &CheClusterReconciler{client: cl, nonCachedClient: cl, Scheme: &scheme, discoveryClient: dc, tests: true, Log: ctrl.Log.WithName("controllers").WithName("CheCluster")}
|
||||
r := NewReconciler(cl, cl, dc, &scheme, "")
|
||||
r.tests = true
|
||||
|
||||
// get CR
|
||||
cheCR := &orgv1.CheCluster{
|
||||
|
|
@ -1343,7 +903,8 @@ func TestConfiguringLabelsForRoutes(t *testing.T) {
|
|||
cl, dc, scheme := Init()
|
||||
|
||||
// Create a ReconcileChe object with the scheme and fake client
|
||||
r := &CheClusterReconciler{client: cl, nonCachedClient: cl, Scheme: &scheme, discoveryClient: dc, tests: true, Log: ctrl.Log.WithName("controllers").WithName("CheCluster")}
|
||||
r := NewReconciler(cl, cl, dc, &scheme, "")
|
||||
r.tests = true
|
||||
|
||||
// get CR
|
||||
cheCR := &orgv1.CheCluster{}
|
||||
|
|
@ -1479,15 +1040,9 @@ func TestShouldDelegatePermissionsForCheWorkspaces(t *testing.T) {
|
|||
defer ctrl.Finish()
|
||||
}
|
||||
|
||||
r := &CheClusterReconciler{
|
||||
client: cli,
|
||||
nonCachedClient: nonCachedClient,
|
||||
discoveryClient: fakeDiscovery,
|
||||
Scheme: scheme,
|
||||
permissionChecker: m,
|
||||
tests: true,
|
||||
Log: ctrl.Log.WithName("controllers").WithName("CheCluster"),
|
||||
}
|
||||
r := NewReconciler(cli, nonCachedClient, fakeDiscovery, scheme, "")
|
||||
r.tests = true
|
||||
|
||||
req := reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{
|
||||
Name: os.Getenv("CHE_FLAVOR"),
|
||||
|
|
@ -1642,261 +1197,3 @@ func InitCheWithSimpleCR() *orgv1.CheCluster {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
func InitCheCRWithImagePullerEnabled() *orgv1.CheCluster {
|
||||
return &orgv1.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: os.Getenv("CHE_FLAVOR"),
|
||||
Namespace: namespace,
|
||||
ResourceVersion: "0",
|
||||
},
|
||||
Spec: orgv1.CheClusterSpec{
|
||||
ImagePuller: orgv1.CheClusterSpecImagePuller{
|
||||
Enable: true,
|
||||
},
|
||||
Server: orgv1.CheClusterSpecServer{
|
||||
ServerExposureStrategy: "multi-host",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func InitCheCRWithImagePullerFinalizer() *orgv1.CheCluster {
|
||||
return &orgv1.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: os.Getenv("CHE_FLAVOR"),
|
||||
Namespace: namespace,
|
||||
Finalizers: []string{
|
||||
"kubernetesimagepullers.finalizers.che.eclipse.org",
|
||||
},
|
||||
ResourceVersion: "1",
|
||||
},
|
||||
Spec: orgv1.CheClusterSpec{
|
||||
ImagePuller: orgv1.CheClusterSpecImagePuller{
|
||||
Enable: true,
|
||||
},
|
||||
Server: orgv1.CheClusterSpecServer{
|
||||
ServerExposureStrategy: "multi-host",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func InitCheCRWithImagePullerFinalizerAndDeletionTimestamp() *orgv1.CheCluster {
|
||||
return &orgv1.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: os.Getenv("CHE_FLAVOR"),
|
||||
Namespace: namespace,
|
||||
Finalizers: []string{
|
||||
"kubernetesimagepullers.finalizers.che.eclipse.org",
|
||||
},
|
||||
DeletionTimestamp: &metav1.Time{Time: time.Unix(1, 0)},
|
||||
ResourceVersion: "1",
|
||||
},
|
||||
Spec: orgv1.CheClusterSpec{
|
||||
ImagePuller: orgv1.CheClusterSpecImagePuller{
|
||||
Enable: true,
|
||||
},
|
||||
Server: orgv1.CheClusterSpecServer{
|
||||
ServerExposureStrategy: "multi-host",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func ExpectedCheCRWithImagePullerFinalizer() *orgv1.CheCluster {
|
||||
return &orgv1.CheCluster{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "CheCluster",
|
||||
APIVersion: "org.eclipse.che/v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: os.Getenv("CHE_FLAVOR"),
|
||||
Namespace: namespace,
|
||||
Finalizers: []string{
|
||||
"kubernetesimagepullers.finalizers.che.eclipse.org",
|
||||
},
|
||||
ResourceVersion: "1",
|
||||
},
|
||||
Spec: orgv1.CheClusterSpec{
|
||||
ImagePuller: orgv1.CheClusterSpecImagePuller{
|
||||
Enable: true,
|
||||
},
|
||||
Server: orgv1.CheClusterSpecServer{
|
||||
ServerExposureStrategy: "multi-host",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func InitCheCRWithImagePullerDisabled() *orgv1.CheCluster {
|
||||
return &orgv1.CheCluster{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "CheCluster",
|
||||
APIVersion: "org.eclipse.che/v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: os.Getenv("CHE_FLAVOR"),
|
||||
Namespace: namespace,
|
||||
ResourceVersion: "1",
|
||||
},
|
||||
Spec: orgv1.CheClusterSpec{
|
||||
ImagePuller: orgv1.CheClusterSpecImagePuller{
|
||||
Enable: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func InitCheCRWithImagePullerEnabledAndDefaultValuesSet() *orgv1.CheCluster {
|
||||
return &orgv1.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: os.Getenv("CHE_FLAVOR"),
|
||||
Namespace: namespace,
|
||||
Finalizers: []string{
|
||||
"kubernetesimagepullers.finalizers.che.eclipse.org",
|
||||
},
|
||||
},
|
||||
Spec: orgv1.CheClusterSpec{
|
||||
ImagePuller: orgv1.CheClusterSpecImagePuller{
|
||||
Enable: true,
|
||||
Spec: chev1alpha1.KubernetesImagePullerSpec{
|
||||
DeploymentName: "kubernetes-image-puller",
|
||||
ConfigMapName: "k8s-image-puller",
|
||||
},
|
||||
},
|
||||
Auth: orgv1.CheClusterSpecAuth{
|
||||
OpenShiftoAuth: util.NewBoolPointer(false),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func InitCheCRWithImagePullerEnabledAndImagesSet(images string) *orgv1.CheCluster {
|
||||
return &orgv1.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: os.Getenv("CHE_FLAVOR"),
|
||||
Namespace: namespace,
|
||||
Finalizers: []string{
|
||||
"kubernetesimagepullers.finalizers.che.eclipse.org",
|
||||
},
|
||||
},
|
||||
Spec: orgv1.CheClusterSpec{
|
||||
ImagePuller: orgv1.CheClusterSpecImagePuller{
|
||||
Enable: true,
|
||||
Spec: chev1alpha1.KubernetesImagePullerSpec{
|
||||
DeploymentName: "kubernetes-image-puller",
|
||||
ConfigMapName: "k8s-image-puller",
|
||||
Images: images,
|
||||
},
|
||||
},
|
||||
Auth: orgv1.CheClusterSpecAuth{
|
||||
OpenShiftoAuth: util.NewBoolPointer(false),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func InitCheCRWithImagePullerEnabledAndNewValuesSet() *orgv1.CheCluster {
|
||||
return &orgv1.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: os.Getenv("CHE_FLAVOR"),
|
||||
Namespace: namespace,
|
||||
Finalizers: []string{
|
||||
"kubernetesimagepullers.finalizers.che.eclipse.org",
|
||||
},
|
||||
},
|
||||
Spec: orgv1.CheClusterSpec{
|
||||
ImagePuller: orgv1.CheClusterSpecImagePuller{
|
||||
Enable: true,
|
||||
Spec: chev1alpha1.KubernetesImagePullerSpec{
|
||||
DeploymentName: "kubernetes-image-puller-trigger-update",
|
||||
ConfigMapName: "k8s-image-puller-trigger-update",
|
||||
},
|
||||
},
|
||||
Auth: orgv1.CheClusterSpecAuth{
|
||||
OpenShiftoAuth: util.NewBoolPointer(false),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type ImagePullerOptions struct {
|
||||
SpecImages string
|
||||
ObjectMetaResourceVersion string
|
||||
}
|
||||
|
||||
func InitImagePuller(options ImagePullerOptions) *chev1alpha1.KubernetesImagePuller {
|
||||
return &chev1alpha1.KubernetesImagePuller{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "che.eclipse.org/v1alpha1",
|
||||
Kind: "KubernetesImagePuller",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: os.Getenv("CHE_FLAVOR") + "-image-puller",
|
||||
Namespace: namespace,
|
||||
Labels: map[string]string{
|
||||
"app.kubernetes.io/part-of": os.Getenv("CHE_FLAVOR"),
|
||||
"app": "che",
|
||||
"component": "kubernetes-image-puller",
|
||||
},
|
||||
ResourceVersion: options.ObjectMetaResourceVersion,
|
||||
OwnerReferences: []metav1.OwnerReference{
|
||||
{
|
||||
APIVersion: "org.eclipse.che/v1",
|
||||
Kind: "CheCluster",
|
||||
Controller: &valueTrue,
|
||||
BlockOwnerDeletion: &valueTrue,
|
||||
Name: os.Getenv("CHE_FLAVOR"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Spec: chev1alpha1.KubernetesImagePullerSpec{
|
||||
DeploymentName: "kubernetes-image-puller",
|
||||
ConfigMapName: "k8s-image-puller",
|
||||
Images: options.SpecImages,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getDefaultImagePuller() *chev1alpha1.KubernetesImagePuller {
|
||||
return &chev1alpha1.KubernetesImagePuller{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "che.eclipse.org/v1alpha1",
|
||||
Kind: "KubernetesImagePuller",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: os.Getenv("CHE_FLAVOR") + "-image-puller",
|
||||
Namespace: namespace,
|
||||
Labels: map[string]string{
|
||||
"app.kubernetes.io/part-of": os.Getenv("CHE_FLAVOR"),
|
||||
"app": "che",
|
||||
"component": "kubernetes-image-puller",
|
||||
},
|
||||
ResourceVersion: "1",
|
||||
OwnerReferences: []metav1.OwnerReference{
|
||||
{
|
||||
APIVersion: "org.eclipse.che/v1",
|
||||
Kind: "CheCluster",
|
||||
Controller: &valueTrue,
|
||||
BlockOwnerDeletion: &valueTrue,
|
||||
Name: os.Getenv("CHE_FLAVOR"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Spec: chev1alpha1.KubernetesImagePullerSpec{
|
||||
DeploymentName: "kubernetes-image-puller",
|
||||
ConfigMapName: "k8s-image-puller",
|
||||
Images: defaultImagePullerImages,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Split string by separator without empty elems
|
||||
func nonEmptySplit(lineToSplit string, separator string) []string {
|
||||
splitFn := func(c rune) bool {
|
||||
runeChar, _ := utf8.DecodeRuneInString(separator)
|
||||
return c == runeChar
|
||||
}
|
||||
return strings.FieldsFunc(lineToSplit, splitFn)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
//
|
||||
// Copyright (c) 2019-2021 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 (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/eclipse-che/che-operator/pkg/deploy"
|
||||
"github.com/eclipse-che/che-operator/pkg/util"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
)
|
||||
|
||||
const (
|
||||
NamespaceStrategyErrorMessage = "Namespace strategies other than 'per user' is not supported anymore. Using the <username> or <userid> placeholder is required in the 'spec.server.workspaceNamespaceDefault' field. The current value is: %s"
|
||||
AuthenticationModeErrorMessage = "Single user authentication mode is not supported anymore. To backup your data you can commit workspace configuration to an SCM server and use factories to restore it in multi user mode. To switch to multi user authentication mode set 'spec.server.customCheProperties.CHE_MULTIUSER' to 'true' in %s CheCluster custom resource. Switching to multi user authentication mode without backing up your data will cause data loss."
|
||||
)
|
||||
|
||||
// CheClusterValidator checks CheCluster CR configuration.
|
||||
// It detect:
|
||||
// - configurations which miss required field(s) to deploy Che
|
||||
// - self-contradictory configurations
|
||||
// - configurations with which it is impossible to deploy Che
|
||||
type CheClusterValidator struct {
|
||||
}
|
||||
|
||||
func NewCheClusterValidator() *CheClusterValidator {
|
||||
return &CheClusterValidator{}
|
||||
}
|
||||
|
||||
func (v *CheClusterValidator) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) {
|
||||
if !util.IsOpenShift {
|
||||
if ctx.CheCluster.Spec.K8s.IngressDomain == "" {
|
||||
return reconcile.Result{}, false, fmt.Errorf("Required field \"spec.K8s.IngressDomain\" is not set")
|
||||
}
|
||||
}
|
||||
|
||||
workspaceNamespaceDefault := util.GetWorkspaceNamespaceDefault(ctx.CheCluster)
|
||||
if strings.Index(workspaceNamespaceDefault, "<username>") == -1 && strings.Index(workspaceNamespaceDefault, "<userid>") == -1 {
|
||||
return reconcile.Result{}, false, fmt.Errorf(NamespaceStrategyErrorMessage, workspaceNamespaceDefault)
|
||||
}
|
||||
|
||||
if !util.IsCheMultiUser(ctx.CheCluster) {
|
||||
return reconcile.Result{}, false, fmt.Errorf(AuthenticationModeErrorMessage, ctx.CheCluster.Name)
|
||||
}
|
||||
|
||||
return reconcile.Result{}, true, nil
|
||||
}
|
||||
|
||||
func (v *CheClusterValidator) Finalize(ctx *deploy.DeployContext) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
|
@ -16,10 +16,9 @@ import (
|
|||
"github.com/eclipse-che/che-operator/pkg/util"
|
||||
"github.com/sirupsen/logrus"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
)
|
||||
|
||||
func (r *CheClusterReconciler) GenerateAndSaveFields(deployContext *deploy.DeployContext, request reconcile.Request) (err error) {
|
||||
func (r *CheClusterReconciler) GenerateAndSaveFields(deployContext *deploy.DeployContext) (err error) {
|
||||
cheFlavor := deploy.DefaultCheFlavor(deployContext.CheCluster)
|
||||
cheNamespace := deployContext.CheCluster.Namespace
|
||||
if len(deployContext.CheCluster.Spec.Server.CheFlavor) < 1 {
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ func (r *CheClusterReconciler) delegateWorkspacePermissionsInTheDifferNamespaceT
|
|||
return false, err
|
||||
}
|
||||
|
||||
done, err = deploy.SyncClusterRoleBindingToCluster(deployContext, сheWorkspacesClusterRoleBindingName, CheServiceAccountName, сheWorkspacesClusterRoleName)
|
||||
done, err = deploy.SyncClusterRoleBindingToCluster(deployContext, сheWorkspacesClusterRoleBindingName, deploy.CheServiceAccountName, сheWorkspacesClusterRoleName)
|
||||
if !done {
|
||||
return false, err
|
||||
}
|
||||
|
|
@ -121,7 +121,7 @@ func (r *CheClusterReconciler) delegateNamespaceEditorPermissions(deployContext
|
|||
return false, err
|
||||
}
|
||||
|
||||
done, err = deploy.SyncClusterRoleBindingToCluster(deployContext, сheNamespaceEditorClusterRoleBindingName, CheServiceAccountName, сheNamespaceEditorClusterRoleName)
|
||||
done, err = deploy.SyncClusterRoleBindingToCluster(deployContext, сheNamespaceEditorClusterRoleBindingName, deploy.CheServiceAccountName, сheNamespaceEditorClusterRoleName)
|
||||
if !done {
|
||||
return false, err
|
||||
}
|
||||
|
|
@ -156,7 +156,7 @@ func (r *CheClusterReconciler) delegateDevWorkspacePermissions(deployContext *de
|
|||
return false, err
|
||||
}
|
||||
|
||||
done, err = deploy.SyncClusterRoleBindingToCluster(deployContext, devWorkspaceClusterRoleBindingName, CheServiceAccountName, devWorkspaceClusterRoleName)
|
||||
done, err = deploy.SyncClusterRoleBindingToCluster(deployContext, devWorkspaceClusterRoleBindingName, deploy.CheServiceAccountName, devWorkspaceClusterRoleName)
|
||||
if !done {
|
||||
return false, err
|
||||
}
|
||||
|
|
|
|||
7
main.go
7
main.go
|
|
@ -34,6 +34,7 @@ import (
|
|||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/healthz"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log/zap"
|
||||
|
||||
|
|
@ -237,11 +238,13 @@ func main() {
|
|||
os.Exit(1)
|
||||
}
|
||||
|
||||
cheReconciler, err := checontroller.NewReconciler(mgr, watchNamespace, discoveryClient)
|
||||
noncachedClient, err := client.New(mgr.GetConfig(), client.Options{Scheme: scheme})
|
||||
if err != nil {
|
||||
setupLog.Error(err, "unable to create checluster reconciler")
|
||||
setupLog.Error(err, "unable to initialize non cached client")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
cheReconciler := checontroller.NewReconciler(mgr.GetClient(), noncachedClient, discoveryClient, mgr.GetScheme(), watchNamespace)
|
||||
backupReconciler := backupcontroller.NewReconciler(mgr, watchNamespace)
|
||||
restoreReconciler := restorecontroller.NewReconciler(mgr, watchNamespace)
|
||||
|
||||
|
|
|
|||
|
|
@ -55,7 +55,3 @@ type Proxy struct {
|
|||
NoProxy string
|
||||
TrustedCAMapName string
|
||||
}
|
||||
|
||||
type Syncable interface {
|
||||
Sync() (bool, error)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -122,6 +122,9 @@ const (
|
|||
PluginRegistryName = "plugin-registry"
|
||||
PostgresName = "postgres"
|
||||
|
||||
// CheServiceAccountName - service account name for che-server.
|
||||
CheServiceAccountName = "che"
|
||||
|
||||
// limits
|
||||
DefaultDashboardMemoryLimit = "256Mi"
|
||||
DefaultDashboardMemoryRequest = "32Mi"
|
||||
|
|
@ -164,6 +167,8 @@ const (
|
|||
GitLabOAuthConfigMountPath = "/che-conf/oauth/gitlab"
|
||||
GitLabOAuthConfigClientIdFileName = "id"
|
||||
GitLabOAuthConfigClientSecretFileName = "secret"
|
||||
|
||||
InstallOrUpdateFailed = "InstallOrUpdateFailed"
|
||||
)
|
||||
|
||||
func InitDefaults(defaultsPath string) {
|
||||
|
|
|
|||
|
|
@ -45,25 +45,35 @@ type ImageAndName struct {
|
|||
Image string // image (ex. quay.io/test/abc)
|
||||
}
|
||||
|
||||
type ImagePuller struct {
|
||||
}
|
||||
|
||||
func NewImagePuller() *ImagePuller {
|
||||
return &ImagePuller{}
|
||||
}
|
||||
|
||||
func (ip *ImagePuller) Reconcile(ctx *DeployContext) (reconcile.Result, bool, error) {
|
||||
return ReconcileImagePuller(ctx)
|
||||
}
|
||||
|
||||
func (ip *ImagePuller) Finalize(ctx *DeployContext) (bool, error) {
|
||||
return DeleteImagePullerOperatorAndFinalizer(ctx)
|
||||
}
|
||||
|
||||
// Reconcile the imagePuller section of the CheCluster CR. If imagePuller.enable is set to true, install the Kubernetes Image Puller operator and create
|
||||
// a KubernetesImagePuller CR. Add a finalizer to the CheCluster CR. If false, remove the KubernetesImagePuller CR, uninstall the operator, and remove the finalizer.
|
||||
func ReconcileImagePuller(ctx *DeployContext) (reconcile.Result, error) {
|
||||
|
||||
if !ctx.CheCluster.ObjectMeta.DeletionTimestamp.IsZero() {
|
||||
return DeleteImagePullerOperatorAndFinalizer(ctx)
|
||||
}
|
||||
|
||||
func ReconcileImagePuller(ctx *DeployContext) (reconcile.Result, bool, error) {
|
||||
// Determine what server groups the API Server knows about
|
||||
foundPackagesAPI, foundOperatorsAPI, _, err := CheckNeededImagePullerApis(ctx)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error discovering image puller APIs: %v", err)
|
||||
return reconcile.Result{}, err
|
||||
return reconcile.Result{}, false, err
|
||||
}
|
||||
|
||||
// If the image puller should be installed but the APIServer doesn't know about PackageManifests/Subscriptions, log a warning and requeue
|
||||
if ctx.CheCluster.Spec.ImagePuller.Enable && (!foundPackagesAPI || !foundOperatorsAPI) {
|
||||
logrus.Infof("Couldn't find Operator Lifecycle Manager types to install the Kubernetes Image Puller Operator. Please install Operator Lifecycle Manager to install the operator or disable the image puller by setting spec.imagePuller.enable to false.")
|
||||
return reconcile.Result{RequeueAfter: time.Second}, nil
|
||||
return reconcile.Result{RequeueAfter: time.Second}, false, nil
|
||||
}
|
||||
|
||||
if ctx.CheCluster.Spec.ImagePuller.Enable {
|
||||
|
|
@ -72,42 +82,42 @@ func ReconcileImagePuller(ctx *DeployContext) (reconcile.Result, error) {
|
|||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
logrus.Infof("There is no PackageManifest for the Kubernetes Image Puller Operator. Install the Operator Lifecycle Manager and the Community Operators Catalog")
|
||||
return reconcile.Result{RequeueAfter: time.Second}, nil
|
||||
return reconcile.Result{RequeueAfter: time.Second}, false, nil
|
||||
}
|
||||
logrus.Errorf("Error getting packagemanifest: %v", err)
|
||||
return reconcile.Result{}, err
|
||||
return reconcile.Result{}, false, err
|
||||
}
|
||||
|
||||
createdOperatorGroup, err := CreateOperatorGroupIfNotFound(ctx)
|
||||
if err != nil {
|
||||
logrus.Infof("Error creating OperatorGroup: %v", err)
|
||||
return reconcile.Result{}, err
|
||||
return reconcile.Result{}, false, err
|
||||
}
|
||||
if createdOperatorGroup {
|
||||
return reconcile.Result{RequeueAfter: time.Second}, nil
|
||||
return reconcile.Result{RequeueAfter: time.Second}, false, nil
|
||||
}
|
||||
createdOperatorSubscription, err := CreateImagePullerSubscription(ctx, packageManifest)
|
||||
if err != nil {
|
||||
logrus.Infof("Error creating Subscription: %v", err)
|
||||
return reconcile.Result{}, err
|
||||
return reconcile.Result{}, false, err
|
||||
}
|
||||
if createdOperatorSubscription {
|
||||
return reconcile.Result{RequeueAfter: time.Second}, nil
|
||||
return reconcile.Result{RequeueAfter: time.Second}, false, nil
|
||||
}
|
||||
|
||||
// Add the image puller finalizer
|
||||
if !HasImagePullerFinalizer(ctx.CheCluster) {
|
||||
if err := ReconcileImagePullerFinalizer(ctx); err != nil {
|
||||
return reconcile.Result{}, err
|
||||
return reconcile.Result{}, false, err
|
||||
}
|
||||
return reconcile.Result{RequeueAfter: time.Second}, nil
|
||||
return reconcile.Result{RequeueAfter: time.Second}, false, nil
|
||||
}
|
||||
}
|
||||
|
||||
_, _, foundKubernetesImagePullerAPI, err := CheckNeededImagePullerApis(ctx)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error discovering image puller APIs: %v", err)
|
||||
return reconcile.Result{}, err
|
||||
return reconcile.Result{}, false, err
|
||||
}
|
||||
// If the KubernetesImagePuller API service exists, attempt to reconcile creation/update
|
||||
if foundKubernetesImagePullerAPI {
|
||||
|
|
@ -125,15 +135,15 @@ func ReconcileImagePuller(ctx *DeployContext) (reconcile.Result, error) {
|
|||
_, err := UpdateImagePullerSpecIfEmpty(ctx)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error updating CheCluster: %v", err)
|
||||
return reconcile.Result{}, err
|
||||
return reconcile.Result{}, false, err
|
||||
}
|
||||
return reconcile.Result{RequeueAfter: time.Second}, nil
|
||||
return reconcile.Result{RequeueAfter: time.Second}, false, nil
|
||||
}
|
||||
|
||||
if ctx.CheCluster.IsImagePullerImagesEmpty() {
|
||||
if err = SetDefaultImages(ctx); err != nil {
|
||||
logrus.Error(err)
|
||||
return reconcile.Result{}, err
|
||||
return reconcile.Result{}, false, err
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -141,19 +151,19 @@ func ReconcileImagePuller(ctx *DeployContext) (reconcile.Result, error) {
|
|||
createdImagePuller, err := CreateKubernetesImagePuller(ctx)
|
||||
if err != nil {
|
||||
logrus.Error("Error creating KubernetesImagePuller: ", err)
|
||||
return reconcile.Result{}, err
|
||||
return reconcile.Result{}, false, err
|
||||
}
|
||||
if createdImagePuller {
|
||||
return reconcile.Result{}, nil
|
||||
return reconcile.Result{}, false, nil
|
||||
}
|
||||
}
|
||||
logrus.Errorf("Error getting KubernetesImagePuller: %v", err)
|
||||
return reconcile.Result{}, err
|
||||
return reconcile.Result{}, false, err
|
||||
}
|
||||
|
||||
if err = UpdateDefaultImagesIfNeeded(ctx); err != nil {
|
||||
logrus.Error(err)
|
||||
return reconcile.Result{}, err
|
||||
return reconcile.Result{}, false, err
|
||||
}
|
||||
|
||||
if ctx.CheCluster.Spec.ImagePuller.Spec.DeploymentName == "" {
|
||||
|
|
@ -172,44 +182,43 @@ func ReconcileImagePuller(ctx *DeployContext) (reconcile.Result, error) {
|
|||
logrus.Infof("Updating KubernetesImagePuller %v", imagePuller.Name)
|
||||
if err = ctx.ClusterAPI.Client.Update(context.TODO(), imagePuller, &client.UpdateOptions{}); err != nil {
|
||||
logrus.Errorf("Error updating KubernetesImagePuller: %v", err)
|
||||
return reconcile.Result{}, err
|
||||
return reconcile.Result{}, false, err
|
||||
}
|
||||
return reconcile.Result{RequeueAfter: time.Second}, nil
|
||||
return reconcile.Result{RequeueAfter: time.Second}, false, nil
|
||||
}
|
||||
} else {
|
||||
logrus.Infof("Waiting 15 seconds for kubernetesimagepullers.che.eclipse.org API")
|
||||
return reconcile.Result{RequeueAfter: 15 * time.Second}, nil
|
||||
return reconcile.Result{RequeueAfter: 15 * time.Second}, false, nil
|
||||
}
|
||||
|
||||
} else {
|
||||
if foundOperatorsAPI && foundPackagesAPI {
|
||||
return DeleteImagePullerOperatorAndFinalizer(ctx)
|
||||
done, err := DeleteImagePullerOperatorAndFinalizer(ctx)
|
||||
if !done {
|
||||
return reconcile.Result{}, false, err
|
||||
} else {
|
||||
return reconcile.Result{}, true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return reconcile.Result{}, nil
|
||||
return reconcile.Result{}, true, nil
|
||||
}
|
||||
|
||||
func DeleteImagePullerOperatorAndFinalizer(ctx *DeployContext) (reconcile.Result, error) {
|
||||
requeue := false
|
||||
func DeleteImagePullerOperatorAndFinalizer(ctx *DeployContext) (bool, error) {
|
||||
if _, err := GetImagePullerOperator(ctx); err == nil {
|
||||
requeue = true
|
||||
if _, err := UninstallImagePullerOperator(ctx); err != nil {
|
||||
logrus.Errorf("Error uninstalling Image Puller: %v", err)
|
||||
return reconcile.Result{}, err
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
if HasImagePullerFinalizer(ctx.CheCluster) {
|
||||
requeue = true
|
||||
if err := DeleteImagePullerFinalizer(ctx); err != nil {
|
||||
logrus.Errorf("Error deleting finalizer: %v", err)
|
||||
return reconcile.Result{}, err
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
if requeue {
|
||||
return reconcile.Result{RequeueAfter: time.Second}, nil
|
||||
}
|
||||
return reconcile.Result{}, nil
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func HasImagePullerFinalizer(instance *orgv1.CheCluster) bool {
|
||||
|
|
|
|||
|
|
@ -12,14 +12,416 @@
|
|||
package deploy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sort"
|
||||
"testing"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
chev1alpha1 "github.com/che-incubator/kubernetes-image-puller-operator/api/v1alpha1"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/eclipse-che/che-operator/pkg/util"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
orgv1 "github.com/eclipse-che/che-operator/api/v1"
|
||||
routev1 "github.com/openshift/api/route/v1"
|
||||
operatorsv1 "github.com/operator-framework/api/pkg/operators/v1"
|
||||
operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
|
||||
packagesv1 "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1"
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
fakeDiscovery "k8s.io/client-go/discovery/fake"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log/zap"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
namespace = "eclipse-che"
|
||||
csvName = "kubernetes-imagepuller-operator.v0.0.9"
|
||||
defaultImagePullerImages string
|
||||
valueTrue = true
|
||||
)
|
||||
|
||||
func TestImagePullerConfiguration(t *testing.T) {
|
||||
oldBrokerMetaDataImage := strings.Split(os.Getenv("RELATED_IMAGE_che_workspace_plugin_broker_metadata"), ":")[0] + ":old"
|
||||
oldBrokerArtifactsImage := strings.Split(os.Getenv("RELATED_IMAGE_che_workspace_plugin_broker_artifacts"), ":")[0] + ":old"
|
||||
type testCase struct {
|
||||
name string
|
||||
initCR *orgv1.CheCluster
|
||||
initObjects []runtime.Object
|
||||
expectedCR *orgv1.CheCluster
|
||||
expectedOperatorGroup *operatorsv1.OperatorGroup
|
||||
expectedSubscription *operatorsv1alpha1.Subscription
|
||||
expectedImagePuller *chev1alpha1.KubernetesImagePuller
|
||||
shouldDelete bool
|
||||
}
|
||||
|
||||
testCases := []testCase{
|
||||
{
|
||||
name: "image puller enabled, no operatorgroup, should create an operatorgroup",
|
||||
initCR: InitCheCRWithImagePullerEnabled(),
|
||||
initObjects: []runtime.Object{
|
||||
getPackageManifest(),
|
||||
},
|
||||
expectedOperatorGroup: getOperatorGroup(),
|
||||
},
|
||||
{
|
||||
name: "image puller enabled, operatorgroup exists, should create a subscription",
|
||||
initCR: InitCheCRWithImagePullerEnabled(),
|
||||
initObjects: []runtime.Object{
|
||||
getPackageManifest(),
|
||||
getOperatorGroup(),
|
||||
},
|
||||
expectedSubscription: getSubscription(),
|
||||
},
|
||||
{
|
||||
name: "image puller enabled, subscription created, should add finalizer",
|
||||
initCR: InitCheCRWithImagePullerEnabled(),
|
||||
expectedCR: ExpectedCheCRWithImagePullerFinalizer(),
|
||||
initObjects: []runtime.Object{
|
||||
getPackageManifest(),
|
||||
getOperatorGroup(),
|
||||
getSubscription(),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "image puller enabled with finalizer but default values are empty, subscription exists, should update the CR",
|
||||
initCR: InitCheCRWithImagePullerFinalizer(),
|
||||
expectedCR: &orgv1.CheCluster{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "CheCluster",
|
||||
APIVersion: "org.eclipse.che/v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: os.Getenv("CHE_FLAVOR"),
|
||||
Namespace: namespace,
|
||||
ResourceVersion: "1",
|
||||
Finalizers: []string{
|
||||
"kubernetesimagepullers.finalizers.che.eclipse.org",
|
||||
},
|
||||
},
|
||||
Spec: orgv1.CheClusterSpec{
|
||||
ImagePuller: orgv1.CheClusterSpecImagePuller{
|
||||
Enable: true,
|
||||
Spec: chev1alpha1.KubernetesImagePullerSpec{
|
||||
DeploymentName: "kubernetes-image-puller",
|
||||
ConfigMapName: "k8s-image-puller",
|
||||
},
|
||||
},
|
||||
Server: orgv1.CheClusterSpecServer{
|
||||
ServerExposureStrategy: "multi-host",
|
||||
},
|
||||
},
|
||||
},
|
||||
initObjects: []runtime.Object{
|
||||
getPackageManifest(),
|
||||
getOperatorGroup(),
|
||||
getSubscription(),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "image puller enabled default values already set, subscription exists, should create a KubernetesImagePuller",
|
||||
initCR: InitCheCRWithImagePullerEnabledAndDefaultValuesSet(),
|
||||
initObjects: []runtime.Object{
|
||||
getPackageManifest(),
|
||||
getOperatorGroup(),
|
||||
getSubscription(),
|
||||
},
|
||||
expectedImagePuller: InitImagePuller(ImagePullerOptions{SpecImages: defaultImagePullerImages, ObjectMetaResourceVersion: "1"}),
|
||||
},
|
||||
{
|
||||
name: "image puller enabled, user images set, subscription exists, should create a KubernetesImagePuller with user images",
|
||||
initCR: InitCheCRWithImagePullerEnabledAndImagesSet("image=image_url"),
|
||||
initObjects: []runtime.Object{
|
||||
getPackageManifest(),
|
||||
getOperatorGroup(),
|
||||
getSubscription(),
|
||||
},
|
||||
expectedImagePuller: InitImagePuller(ImagePullerOptions{SpecImages: "image=image_url", ObjectMetaResourceVersion: "1"}),
|
||||
},
|
||||
{
|
||||
name: "image puller enabled, one default image set, subscription exists, should update KubernetesImagePuller default image",
|
||||
initCR: InitCheCRWithImagePullerEnabledAndImagesSet("che-workspace-plugin-broker-metadata=" + oldBrokerMetaDataImage + ";"),
|
||||
initObjects: []runtime.Object{
|
||||
getPackageManifest(),
|
||||
getOperatorGroup(),
|
||||
getSubscription(),
|
||||
InitImagePuller(ImagePullerOptions{SpecImages: "che-workspace-plugin-broker-metadata=" + oldBrokerMetaDataImage + ";", ObjectMetaResourceVersion: "1"}),
|
||||
},
|
||||
expectedImagePuller: InitImagePuller(ImagePullerOptions{SpecImages: "che-workspace-plugin-broker-metadata=" + os.Getenv("RELATED_IMAGE_che_workspace_plugin_broker_metadata") + ";", ObjectMetaResourceVersion: "2"}),
|
||||
},
|
||||
{
|
||||
name: "image puller enabled, one default image set, subscription exists, should update KubernetesImagePuller default images while keeping user image",
|
||||
initCR: InitCheCRWithImagePullerEnabledAndImagesSet("image=image_url;che-workspace-plugin-broker-metadata=" + oldBrokerMetaDataImage + ";"),
|
||||
initObjects: []runtime.Object{
|
||||
getPackageManifest(),
|
||||
getOperatorGroup(),
|
||||
getSubscription(),
|
||||
InitImagePuller(ImagePullerOptions{SpecImages: "image=image_url;che-workspace-plugin-broker-metadata=" + oldBrokerMetaDataImage + ";", ObjectMetaResourceVersion: "1"}),
|
||||
},
|
||||
expectedImagePuller: InitImagePuller(ImagePullerOptions{SpecImages: "image=image_url;che-workspace-plugin-broker-metadata=" + os.Getenv("RELATED_IMAGE_che_workspace_plugin_broker_metadata") + ";", ObjectMetaResourceVersion: "2"}),
|
||||
},
|
||||
{
|
||||
name: "image puller enabled, default images set, subscription exists, should update KubernetesImagePuller default images",
|
||||
initCR: InitCheCRWithImagePullerEnabledAndImagesSet("che-workspace-plugin-broker-metadata=" + oldBrokerMetaDataImage + ";che-workspace-plugin-broker-artifacts=" + oldBrokerArtifactsImage + ";"),
|
||||
initObjects: []runtime.Object{
|
||||
getPackageManifest(),
|
||||
getOperatorGroup(),
|
||||
getSubscription(),
|
||||
InitImagePuller(ImagePullerOptions{SpecImages: "che-workspace-plugin-broker-metadata=" + oldBrokerMetaDataImage + ";che-workspace-plugin-broker-artifacts=" + oldBrokerArtifactsImage + ";", ObjectMetaResourceVersion: "1"}),
|
||||
},
|
||||
expectedImagePuller: InitImagePuller(ImagePullerOptions{SpecImages: defaultImagePullerImages, ObjectMetaResourceVersion: "2"}),
|
||||
},
|
||||
{
|
||||
name: "image puller enabled, latest default images set, subscription exists, should not update KubernetesImagePuller default images",
|
||||
initCR: InitCheCRWithImagePullerEnabledAndImagesSet(defaultImagePullerImages),
|
||||
initObjects: []runtime.Object{
|
||||
getPackageManifest(),
|
||||
getOperatorGroup(),
|
||||
getSubscription(),
|
||||
InitImagePuller(ImagePullerOptions{SpecImages: defaultImagePullerImages, ObjectMetaResourceVersion: "1"}),
|
||||
},
|
||||
expectedImagePuller: InitImagePuller(ImagePullerOptions{SpecImages: defaultImagePullerImages, ObjectMetaResourceVersion: "1"}),
|
||||
},
|
||||
{
|
||||
name: "image puller enabled, default images not set, subscription exists, should not set KubernetesImagePuller default images",
|
||||
initCR: InitCheCRWithImagePullerEnabledAndImagesSet("image=image_url;"),
|
||||
initObjects: []runtime.Object{
|
||||
getPackageManifest(),
|
||||
getOperatorGroup(),
|
||||
getSubscription(),
|
||||
InitImagePuller(ImagePullerOptions{SpecImages: "image=image_url;", ObjectMetaResourceVersion: "1"}),
|
||||
},
|
||||
expectedImagePuller: InitImagePuller(ImagePullerOptions{SpecImages: "image=image_url;", ObjectMetaResourceVersion: "1"}),
|
||||
},
|
||||
{
|
||||
name: "image puller enabled, KubernetesImagePuller created and spec in CheCluster is different, should update the KubernetesImagePuller",
|
||||
initCR: InitCheCRWithImagePullerEnabledAndNewValuesSet(),
|
||||
initObjects: []runtime.Object{
|
||||
getPackageManifest(),
|
||||
getOperatorGroup(),
|
||||
getSubscription(),
|
||||
getDefaultImagePuller(),
|
||||
},
|
||||
expectedImagePuller: &chev1alpha1.KubernetesImagePuller{
|
||||
TypeMeta: metav1.TypeMeta{Kind: "KubernetesImagePuller", APIVersion: "che.eclipse.org/v1alpha1"},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
ResourceVersion: "2",
|
||||
Name: os.Getenv("CHE_FLAVOR") + "-image-puller",
|
||||
Namespace: namespace,
|
||||
Labels: map[string]string{
|
||||
"app": "che",
|
||||
"component": "kubernetes-image-puller",
|
||||
"app.kubernetes.io/part-of": os.Getenv("CHE_FLAVOR"),
|
||||
},
|
||||
OwnerReferences: []metav1.OwnerReference{
|
||||
{
|
||||
APIVersion: "org.eclipse.che/v1",
|
||||
Kind: "CheCluster",
|
||||
BlockOwnerDeletion: &valueTrue,
|
||||
Controller: &valueTrue,
|
||||
Name: os.Getenv("CHE_FLAVOR"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Spec: chev1alpha1.KubernetesImagePullerSpec{
|
||||
ConfigMapName: "k8s-image-puller-trigger-update",
|
||||
DeploymentName: "kubernetes-image-puller-trigger-update",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "image puller already created, imagePuller disabled, should delete everything",
|
||||
initCR: InitCheCRWithImagePullerDisabled(),
|
||||
initObjects: []runtime.Object{
|
||||
getPackageManifest(),
|
||||
getOperatorGroup(),
|
||||
getSubscription(),
|
||||
getClusterServiceVersion(),
|
||||
getDefaultImagePuller(),
|
||||
},
|
||||
expectedCR: InitCheCRWithImagePullerDisabled(),
|
||||
shouldDelete: true,
|
||||
},
|
||||
{
|
||||
name: "image puller already created, finalizer deleted",
|
||||
initCR: InitCheCRWithImagePullerFinalizerAndDeletionTimestamp(),
|
||||
initObjects: []runtime.Object{
|
||||
getPackageManifest(),
|
||||
getOperatorGroup(),
|
||||
getSubscription(),
|
||||
getClusterServiceVersion(),
|
||||
getDefaultImagePuller(),
|
||||
},
|
||||
shouldDelete: true,
|
||||
expectedCR: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true)))
|
||||
|
||||
deployContext := GetTestDeployContext(testCase.initCR, []runtime.Object{})
|
||||
|
||||
orgv1.SchemeBuilder.AddToScheme(deployContext.ClusterAPI.Scheme)
|
||||
packagesv1.AddToScheme(deployContext.ClusterAPI.Scheme)
|
||||
operatorsv1alpha1.AddToScheme(deployContext.ClusterAPI.Scheme)
|
||||
operatorsv1.AddToScheme(deployContext.ClusterAPI.Scheme)
|
||||
chev1alpha1.AddToScheme(deployContext.ClusterAPI.Scheme)
|
||||
routev1.AddToScheme(deployContext.ClusterAPI.Scheme)
|
||||
|
||||
for _, obj := range testCase.initObjects {
|
||||
obj.(metav1.Object).SetResourceVersion("")
|
||||
err := deployContext.ClusterAPI.NonCachedClient.Create(context.TODO(), obj.(client.Object))
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
deployContext.ClusterAPI.DiscoveryClient.(*fakeDiscovery.FakeDiscovery).Fake.Resources = []*metav1.APIResourceList{
|
||||
{
|
||||
GroupVersion: "packages.operators.coreos.com/v1",
|
||||
APIResources: []metav1.APIResource{
|
||||
{
|
||||
Kind: "PackageManifest",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
GroupVersion: "operators.coreos.com/v1alpha1",
|
||||
APIResources: []metav1.APIResource{
|
||||
{Kind: "OperatorGroup"},
|
||||
{Kind: "Subscription"},
|
||||
{Kind: "ClusterServiceVersion"},
|
||||
},
|
||||
},
|
||||
{
|
||||
GroupVersion: "che.eclipse.org/v1alpha1",
|
||||
APIResources: []metav1.APIResource{
|
||||
{Kind: "KubernetesImagePuller"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var err error
|
||||
if testCase.shouldDelete {
|
||||
_, err = DeleteImagePullerOperatorAndFinalizer(deployContext)
|
||||
if err != nil {
|
||||
t.Fatalf("Error reconciling: %v", err)
|
||||
}
|
||||
} else {
|
||||
_, _, err = ReconcileImagePuller(deployContext)
|
||||
if err != nil {
|
||||
t.Fatalf("Error reconciling: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if testCase.expectedOperatorGroup != nil {
|
||||
gotOperatorGroup := &operatorsv1.OperatorGroup{}
|
||||
err := deployContext.ClusterAPI.NonCachedClient.Get(context.TODO(), types.NamespacedName{Namespace: testCase.expectedOperatorGroup.Namespace, Name: testCase.expectedOperatorGroup.Name}, gotOperatorGroup)
|
||||
if err != nil {
|
||||
t.Errorf("Error getting OperatorGroup: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(testCase.expectedOperatorGroup.Spec.TargetNamespaces, gotOperatorGroup.Spec.TargetNamespaces) {
|
||||
t.Errorf("Error expected target namespace %v but got %v", testCase.expectedOperatorGroup.Spec.TargetNamespaces, gotOperatorGroup.Spec.TargetNamespaces)
|
||||
}
|
||||
}
|
||||
if testCase.expectedSubscription != nil {
|
||||
gotSubscription := &operatorsv1alpha1.Subscription{}
|
||||
err := deployContext.ClusterAPI.NonCachedClient.Get(context.TODO(), types.NamespacedName{Namespace: testCase.expectedSubscription.Namespace, Name: testCase.expectedSubscription.Name}, gotSubscription)
|
||||
if err != nil {
|
||||
t.Errorf("Error getting Subscription: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(testCase.expectedSubscription.Spec, gotSubscription.Spec) {
|
||||
t.Errorf("Error, subscriptions differ (-want +got) %v", cmp.Diff(testCase.expectedSubscription.Spec, gotSubscription.Spec))
|
||||
}
|
||||
}
|
||||
// if expectedCR is not set, don't check it
|
||||
if testCase.expectedCR != nil && !reflect.DeepEqual(testCase.initCR, testCase.expectedCR) {
|
||||
gotCR := &orgv1.CheCluster{}
|
||||
err = deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: os.Getenv("CHE_FLAVOR")}, gotCR)
|
||||
if err != nil {
|
||||
t.Errorf("Error getting CheCluster: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(testCase.expectedCR, gotCR) {
|
||||
t.Errorf("Expected CR and CR returned from API server are different (-want +got): %v", cmp.Diff(testCase.expectedCR, gotCR))
|
||||
}
|
||||
}
|
||||
if testCase.expectedImagePuller != nil {
|
||||
gotImagePuller := &chev1alpha1.KubernetesImagePuller{}
|
||||
err = deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Namespace: testCase.expectedImagePuller.Namespace, Name: testCase.expectedImagePuller.Name}, gotImagePuller)
|
||||
if err != nil {
|
||||
t.Errorf("Error getting KubernetesImagePuller: %v", err)
|
||||
}
|
||||
|
||||
diff := cmp.Diff(testCase.expectedImagePuller, gotImagePuller, cmpopts.IgnoreFields(chev1alpha1.KubernetesImagePullerSpec{}, "Images"))
|
||||
if diff != "" {
|
||||
t.Errorf("Expected KubernetesImagePuller and KubernetesImagePuller returned from API server differ (-want, +got): %v", diff)
|
||||
}
|
||||
|
||||
expectedImages := nonEmptySplit(testCase.expectedImagePuller.Spec.Images, ";")
|
||||
if len(nonEmptySplit(testCase.expectedImagePuller.Spec.Images, ";")) != len(expectedImages) {
|
||||
t.Errorf("Expected KubernetesImagePuller returns %d images", len(expectedImages))
|
||||
}
|
||||
|
||||
for _, expectedImage := range expectedImages {
|
||||
if !strings.Contains(gotImagePuller.Spec.Images, expectedImage) {
|
||||
t.Errorf("Expected KubernetesImagePuller returned image: %s, but it did not", expectedImage)
|
||||
}
|
||||
}
|
||||
}
|
||||
if testCase.shouldDelete {
|
||||
if testCase.expectedCR == nil {
|
||||
gotCR := &orgv1.CheCluster{}
|
||||
err = deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: os.Getenv("CHE_FLAVOR")}, gotCR)
|
||||
if !errors.IsNotFound(err) {
|
||||
t.Fatal("CR CheCluster should be removed")
|
||||
}
|
||||
}
|
||||
|
||||
imagePuller := &chev1alpha1.KubernetesImagePuller{}
|
||||
err = deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: os.Getenv("CHE_FLAVOR") + "-image-puller"}, imagePuller)
|
||||
if err == nil || !errors.IsNotFound(err) {
|
||||
t.Fatalf("Should not have found KubernetesImagePuller: %v", err)
|
||||
}
|
||||
|
||||
clusterServiceVersion := &operatorsv1alpha1.ClusterServiceVersion{}
|
||||
err = deployContext.ClusterAPI.NonCachedClient.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: csvName}, clusterServiceVersion)
|
||||
if err == nil || !errors.IsNotFound(err) {
|
||||
t.Fatalf("Should not have found ClusterServiceVersion: %v", err)
|
||||
}
|
||||
|
||||
subscription := &operatorsv1alpha1.Subscription{}
|
||||
err = deployContext.ClusterAPI.NonCachedClient.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: "kubernetes-imagepuller-operator"}, subscription)
|
||||
if err == nil || !errors.IsNotFound(err) {
|
||||
t.Fatalf("Should not have found Subscription: %v", err)
|
||||
}
|
||||
|
||||
operatorGroup := &operatorsv1.OperatorGroup{}
|
||||
err = deployContext.ClusterAPI.NonCachedClient.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: "kubernetes-imagepuller-operator"}, operatorGroup)
|
||||
if err == nil || !errors.IsNotFound(err) {
|
||||
t.Fatalf("Should not have found OperatorGroup: %v", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnvVars(t *testing.T) {
|
||||
type testcase struct {
|
||||
name string
|
||||
|
|
@ -139,3 +541,341 @@ func sortImages(images []ImageAndName) []ImageAndName {
|
|||
})
|
||||
return imagesCopy
|
||||
}
|
||||
|
||||
func InitCheCRWithImagePullerEnabled() *orgv1.CheCluster {
|
||||
return &orgv1.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: os.Getenv("CHE_FLAVOR"),
|
||||
Namespace: namespace,
|
||||
ResourceVersion: "0",
|
||||
},
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "org.eclipse.che/v1",
|
||||
Kind: "CheCluster",
|
||||
},
|
||||
Spec: orgv1.CheClusterSpec{
|
||||
ImagePuller: orgv1.CheClusterSpecImagePuller{
|
||||
Enable: true,
|
||||
},
|
||||
Server: orgv1.CheClusterSpecServer{
|
||||
ServerExposureStrategy: "multi-host",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func InitCheCRWithImagePullerFinalizer() *orgv1.CheCluster {
|
||||
return &orgv1.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: os.Getenv("CHE_FLAVOR"),
|
||||
Namespace: namespace,
|
||||
Finalizers: []string{
|
||||
"kubernetesimagepullers.finalizers.che.eclipse.org",
|
||||
},
|
||||
ResourceVersion: "0",
|
||||
},
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "org.eclipse.che/v1",
|
||||
Kind: "CheCluster",
|
||||
},
|
||||
Spec: orgv1.CheClusterSpec{
|
||||
ImagePuller: orgv1.CheClusterSpecImagePuller{
|
||||
Enable: true,
|
||||
},
|
||||
Server: orgv1.CheClusterSpecServer{
|
||||
ServerExposureStrategy: "multi-host",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func InitCheCRWithImagePullerFinalizerAndDeletionTimestamp() *orgv1.CheCluster {
|
||||
return &orgv1.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: os.Getenv("CHE_FLAVOR"),
|
||||
Namespace: namespace,
|
||||
Finalizers: []string{
|
||||
"kubernetesimagepullers.finalizers.che.eclipse.org",
|
||||
},
|
||||
DeletionTimestamp: &metav1.Time{Time: time.Unix(1, 0)},
|
||||
},
|
||||
Spec: orgv1.CheClusterSpec{
|
||||
ImagePuller: orgv1.CheClusterSpecImagePuller{
|
||||
Enable: true,
|
||||
},
|
||||
Server: orgv1.CheClusterSpecServer{
|
||||
ServerExposureStrategy: "multi-host",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func ExpectedCheCRWithImagePullerFinalizer() *orgv1.CheCluster {
|
||||
return &orgv1.CheCluster{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "CheCluster",
|
||||
APIVersion: "org.eclipse.che/v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: os.Getenv("CHE_FLAVOR"),
|
||||
Namespace: namespace,
|
||||
Finalizers: []string{
|
||||
"kubernetesimagepullers.finalizers.che.eclipse.org",
|
||||
},
|
||||
ResourceVersion: "1",
|
||||
},
|
||||
Spec: orgv1.CheClusterSpec{
|
||||
ImagePuller: orgv1.CheClusterSpecImagePuller{
|
||||
Enable: true,
|
||||
},
|
||||
Server: orgv1.CheClusterSpecServer{
|
||||
ServerExposureStrategy: "multi-host",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func InitCheCRWithImagePullerDisabled() *orgv1.CheCluster {
|
||||
return &orgv1.CheCluster{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "CheCluster",
|
||||
APIVersion: "org.eclipse.che/v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: os.Getenv("CHE_FLAVOR"),
|
||||
Namespace: namespace,
|
||||
ResourceVersion: "0",
|
||||
},
|
||||
Spec: orgv1.CheClusterSpec{
|
||||
ImagePuller: orgv1.CheClusterSpecImagePuller{
|
||||
Enable: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func InitCheCRWithImagePullerEnabledAndDefaultValuesSet() *orgv1.CheCluster {
|
||||
return &orgv1.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: os.Getenv("CHE_FLAVOR"),
|
||||
Namespace: namespace,
|
||||
Finalizers: []string{
|
||||
"kubernetesimagepullers.finalizers.che.eclipse.org",
|
||||
},
|
||||
},
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "org.eclipse.che/v1",
|
||||
Kind: "CheCluster",
|
||||
},
|
||||
Spec: orgv1.CheClusterSpec{
|
||||
ImagePuller: orgv1.CheClusterSpecImagePuller{
|
||||
Enable: true,
|
||||
Spec: chev1alpha1.KubernetesImagePullerSpec{
|
||||
DeploymentName: "kubernetes-image-puller",
|
||||
ConfigMapName: "k8s-image-puller",
|
||||
},
|
||||
},
|
||||
Auth: orgv1.CheClusterSpecAuth{
|
||||
OpenShiftoAuth: util.NewBoolPointer(false),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func InitCheCRWithImagePullerEnabledAndImagesSet(images string) *orgv1.CheCluster {
|
||||
return &orgv1.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: os.Getenv("CHE_FLAVOR"),
|
||||
Namespace: namespace,
|
||||
Finalizers: []string{
|
||||
"kubernetesimagepullers.finalizers.che.eclipse.org",
|
||||
},
|
||||
},
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "org.eclipse.che/v1",
|
||||
Kind: "CheCluster",
|
||||
},
|
||||
Spec: orgv1.CheClusterSpec{
|
||||
ImagePuller: orgv1.CheClusterSpecImagePuller{
|
||||
Enable: true,
|
||||
Spec: chev1alpha1.KubernetesImagePullerSpec{
|
||||
DeploymentName: "kubernetes-image-puller",
|
||||
ConfigMapName: "k8s-image-puller",
|
||||
Images: images,
|
||||
},
|
||||
},
|
||||
Auth: orgv1.CheClusterSpecAuth{
|
||||
OpenShiftoAuth: util.NewBoolPointer(false),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func InitCheCRWithImagePullerEnabledAndNewValuesSet() *orgv1.CheCluster {
|
||||
return &orgv1.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: os.Getenv("CHE_FLAVOR"),
|
||||
Namespace: namespace,
|
||||
Finalizers: []string{
|
||||
"kubernetesimagepullers.finalizers.che.eclipse.org",
|
||||
},
|
||||
},
|
||||
Spec: orgv1.CheClusterSpec{
|
||||
ImagePuller: orgv1.CheClusterSpecImagePuller{
|
||||
Enable: true,
|
||||
Spec: chev1alpha1.KubernetesImagePullerSpec{
|
||||
DeploymentName: "kubernetes-image-puller-trigger-update",
|
||||
ConfigMapName: "k8s-image-puller-trigger-update",
|
||||
},
|
||||
},
|
||||
Auth: orgv1.CheClusterSpecAuth{
|
||||
OpenShiftoAuth: util.NewBoolPointer(false),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type ImagePullerOptions struct {
|
||||
SpecImages string
|
||||
ObjectMetaResourceVersion string
|
||||
}
|
||||
|
||||
func InitImagePuller(options ImagePullerOptions) *chev1alpha1.KubernetesImagePuller {
|
||||
return &chev1alpha1.KubernetesImagePuller{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "che.eclipse.org/v1alpha1",
|
||||
Kind: "KubernetesImagePuller",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: os.Getenv("CHE_FLAVOR") + "-image-puller",
|
||||
Namespace: namespace,
|
||||
ResourceVersion: options.ObjectMetaResourceVersion,
|
||||
Labels: map[string]string{
|
||||
"app.kubernetes.io/part-of": os.Getenv("CHE_FLAVOR"),
|
||||
"app": "che",
|
||||
"component": "kubernetes-image-puller",
|
||||
},
|
||||
OwnerReferences: []metav1.OwnerReference{
|
||||
{
|
||||
APIVersion: "org.eclipse.che/v1",
|
||||
Kind: "CheCluster",
|
||||
Controller: &valueTrue,
|
||||
BlockOwnerDeletion: &valueTrue,
|
||||
Name: os.Getenv("CHE_FLAVOR"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Spec: chev1alpha1.KubernetesImagePullerSpec{
|
||||
DeploymentName: "kubernetes-image-puller",
|
||||
ConfigMapName: "k8s-image-puller",
|
||||
Images: options.SpecImages,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getDefaultImagePuller() *chev1alpha1.KubernetesImagePuller {
|
||||
return &chev1alpha1.KubernetesImagePuller{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "che.eclipse.org/v1alpha1",
|
||||
Kind: "KubernetesImagePuller",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: os.Getenv("CHE_FLAVOR") + "-image-puller",
|
||||
Namespace: namespace,
|
||||
Labels: map[string]string{
|
||||
"app.kubernetes.io/part-of": os.Getenv("CHE_FLAVOR"),
|
||||
"app": "che",
|
||||
"component": "kubernetes-image-puller",
|
||||
},
|
||||
OwnerReferences: []metav1.OwnerReference{
|
||||
{
|
||||
APIVersion: "org.eclipse.che/v1",
|
||||
Kind: "CheCluster",
|
||||
Controller: &valueTrue,
|
||||
BlockOwnerDeletion: &valueTrue,
|
||||
Name: os.Getenv("CHE_FLAVOR"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Spec: chev1alpha1.KubernetesImagePullerSpec{
|
||||
DeploymentName: "kubernetes-image-puller",
|
||||
ConfigMapName: "k8s-image-puller",
|
||||
Images: defaultImagePullerImages,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Split string by separator without empty elems
|
||||
func nonEmptySplit(lineToSplit string, separator string) []string {
|
||||
splitFn := func(c rune) bool {
|
||||
runeChar, _ := utf8.DecodeRuneInString(separator)
|
||||
return c == runeChar
|
||||
}
|
||||
return strings.FieldsFunc(lineToSplit, splitFn)
|
||||
}
|
||||
|
||||
func getPackageManifest() *packagesv1.PackageManifest {
|
||||
return &packagesv1.PackageManifest{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "kubernetes-imagepuller-operator",
|
||||
Namespace: namespace,
|
||||
},
|
||||
Status: packagesv1.PackageManifestStatus{
|
||||
CatalogSource: "community-operators",
|
||||
CatalogSourceNamespace: "olm",
|
||||
DefaultChannel: "stable",
|
||||
PackageName: "kubernetes-imagepuller-operator",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getOperatorGroup() *operatorsv1.OperatorGroup {
|
||||
return &operatorsv1.OperatorGroup{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "kubernetes-imagepuller-operator",
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: operatorsv1.OperatorGroupSpec{
|
||||
TargetNamespaces: []string{
|
||||
namespace,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
func getSubscription() *operatorsv1alpha1.Subscription {
|
||||
return &operatorsv1alpha1.Subscription{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "kubernetes-imagepuller-operator",
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: &operatorsv1alpha1.SubscriptionSpec{
|
||||
CatalogSource: "community-operators",
|
||||
Channel: "stable",
|
||||
CatalogSourceNamespace: "olm",
|
||||
InstallPlanApproval: operatorsv1alpha1.ApprovalAutomatic,
|
||||
Package: "kubernetes-imagepuller-operator",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getClusterServiceVersion() *operatorsv1alpha1.ClusterServiceVersion {
|
||||
return &operatorsv1alpha1.ClusterServiceVersion{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: namespace,
|
||||
Name: csvName,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
operator := &appsv1.Deployment{}
|
||||
data, err := ioutil.ReadFile("../../config/manager/manager.yaml")
|
||||
yaml.Unmarshal(data, operator)
|
||||
if err == nil {
|
||||
for _, env := range operator.Spec.Template.Spec.Containers[0].Env {
|
||||
os.Setenv(env.Name, env.Value)
|
||||
}
|
||||
}
|
||||
defaultImagePullerImages = "che-workspace-plugin-broker-metadata=" + os.Getenv("RELATED_IMAGE_che_workspace_plugin_broker_metadata") +
|
||||
";che-workspace-plugin-broker-artifacts=" + os.Getenv("RELATED_IMAGE_che_workspace_plugin_broker_artifacts") + ";"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
//
|
||||
// Copyright (c) 2019-2021 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 (
|
||||
"reflect"
|
||||
"runtime"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
)
|
||||
|
||||
type Reconcilable interface {
|
||||
// Reconcile object.
|
||||
Reconcile(ctx *DeployContext) (result reconcile.Result, done bool, err error)
|
||||
// Does finalization (removes cluster scope objects, etc)
|
||||
Finalize(ctx *DeployContext) (done bool, err error)
|
||||
}
|
||||
|
||||
type ReconcileManager struct {
|
||||
reconcilers []Reconcilable
|
||||
failedReconciler Reconcilable
|
||||
}
|
||||
|
||||
func NewReconcileManager() *ReconcileManager {
|
||||
return &ReconcileManager{
|
||||
reconcilers: make([]Reconcilable, 0),
|
||||
failedReconciler: nil,
|
||||
}
|
||||
}
|
||||
|
||||
func (manager *ReconcileManager) RegisterReconciler(reconciler Reconcilable) {
|
||||
manager.reconcilers = append(manager.reconcilers, reconciler)
|
||||
}
|
||||
|
||||
// Reconcile all objects in a order they have been added
|
||||
// If reconciliation failed then CheCluster status will be updated accordingly.
|
||||
func (manager *ReconcileManager) ReconcileAll(ctx *DeployContext) (reconcile.Result, bool, error) {
|
||||
for _, reconciler := range manager.reconcilers {
|
||||
result, done, err := reconciler.Reconcile(ctx)
|
||||
if err != nil {
|
||||
manager.failedReconciler = reconciler
|
||||
if err := SetStatusDetails(ctx, InstallOrUpdateFailed, err.Error(), ""); err != nil {
|
||||
logrus.Errorf("Failed to update checluster status, cause: %v", err)
|
||||
}
|
||||
} else if manager.failedReconciler == reconciler {
|
||||
manager.failedReconciler = nil
|
||||
if err := SetStatusDetails(ctx, "", "", ""); err != nil {
|
||||
logrus.Errorf("Failed to update checluster status, cause: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if !done {
|
||||
return result, done, err
|
||||
}
|
||||
}
|
||||
|
||||
return reconcile.Result{}, true, nil
|
||||
}
|
||||
|
||||
func (manager *ReconcileManager) FinalizeAll(ctx *DeployContext) {
|
||||
for _, reconciler := range manager.reconcilers {
|
||||
_, err := reconciler.Finalize(ctx)
|
||||
if err != nil {
|
||||
reconcilerName := runtime.FuncForPC(reflect.ValueOf(reconciler).Pointer()).Name()
|
||||
logrus.Errorf("Finalization failed for reconciler: `%s`, cause: %v", reconcilerName, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
//
|
||||
// Copyright (c) 2019-2021 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 (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
)
|
||||
|
||||
type TestReconcilable struct {
|
||||
shouldFailReconcileOnce bool
|
||||
alreadyFailed bool
|
||||
}
|
||||
|
||||
func NewTestReconcilable(shouldFailReconcileOnce bool) *TestReconcilable {
|
||||
return &TestReconcilable{shouldFailReconcileOnce, false}
|
||||
}
|
||||
|
||||
func (tr *TestReconcilable) Reconcile(ctx *DeployContext) (reconcile.Result, bool, error) {
|
||||
// Fails on first invocation passes on others
|
||||
if !tr.alreadyFailed && tr.shouldFailReconcileOnce {
|
||||
tr.alreadyFailed = true
|
||||
return reconcile.Result{}, false, fmt.Errorf("Reconcile error")
|
||||
} else {
|
||||
return reconcile.Result{}, true, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (tr *TestReconcilable) Finalize(ctx *DeployContext) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func TestShouldUpdateAndCleanStatus(t *testing.T) {
|
||||
deployContext := GetTestDeployContext(nil, []runtime.Object{})
|
||||
|
||||
tr := NewTestReconcilable(true)
|
||||
|
||||
rm := NewReconcileManager()
|
||||
rm.RegisterReconciler(tr)
|
||||
|
||||
_, done, err := rm.ReconcileAll(deployContext)
|
||||
|
||||
assert.False(t, done)
|
||||
assert.NotNil(t, err)
|
||||
assert.NotEmpty(t, deployContext.CheCluster.Status.Reason)
|
||||
assert.Equal(t, "Reconcile error", deployContext.CheCluster.Status.Message)
|
||||
assert.Equal(t, tr, rm.failedReconciler)
|
||||
|
||||
_, done, err = rm.ReconcileAll(deployContext)
|
||||
|
||||
assert.True(t, done)
|
||||
assert.Nil(t, err)
|
||||
assert.Empty(t, deployContext.CheCluster.Status.Reason)
|
||||
assert.Empty(t, deployContext.CheCluster.Status.Message)
|
||||
assert.Nil(t, rm.failedReconciler)
|
||||
}
|
||||
|
|
@ -31,6 +31,10 @@ func GetTestDeployContext(cheCluster *orgv1.CheCluster, initObjs []runtime.Objec
|
|||
Name: "eclipse-che",
|
||||
Namespace: "eclipse-che",
|
||||
},
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "org.eclipse.che/v1",
|
||||
Kind: "CheCluster",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue