chore: Refactor Kubernetes Image Puller (#1598)
* chore: Refactor Kubernetes Image Puller Signed-off-by: Anatolii Bazko <abazko@redhat.com>pull/1600/head
parent
3d07ff759a
commit
4e75590a11
|
|
@ -119,9 +119,6 @@ const (
|
|||
DefaultCheTLSSecretName = "che-tls"
|
||||
DefaultIngressClass = "nginx"
|
||||
|
||||
// ImagePuller
|
||||
KubernetesImagePullerOperatorCSV = "kubernetes-imagepuller-operator.v0.0.9"
|
||||
|
||||
// components name
|
||||
DevfileRegistryName = "devfile-registry"
|
||||
PluginRegistryName = "plugin-registry"
|
||||
|
|
@ -130,6 +127,7 @@ const (
|
|||
GatewayConfigSideCarContainerName = "configbump"
|
||||
GatewayAuthenticationContainerName = "oauth-proxy"
|
||||
GatewayAuthorizationContainerName = "kube-rbac-proxy"
|
||||
KubernetesImagePullerComponentName = "kubernetes-image-puller"
|
||||
|
||||
// common
|
||||
CheFlavor = "che"
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/utils/pointer"
|
||||
|
||||
chev1alpha1 "github.com/che-incubator/kubernetes-image-puller-operator/api/v1alpha1"
|
||||
chev2 "github.com/eclipse-che/che-operator/api/v2"
|
||||
"github.com/eclipse-che/che-operator/pkg/common/chetypes"
|
||||
console "github.com/openshift/api/console/v1"
|
||||
|
|
@ -184,6 +185,7 @@ func GetDeployContext(cheCluster *chev2.CheCluster, initObjs []runtime.Object) *
|
|||
scheme.AddKnownTypes(corev1.SchemeGroupVersion, &corev1.Secret{})
|
||||
scheme.AddKnownTypes(corev1.SchemeGroupVersion, &corev1.Secret{})
|
||||
scheme.AddKnownTypes(console.GroupVersion, &console.ConsoleLink{})
|
||||
scheme.AddKnownTypes(chev1alpha1.GroupVersion, &chev1alpha1.KubernetesImagePuller{})
|
||||
securityv1.Install(scheme)
|
||||
|
||||
initObjs = append(initObjs, cheCluster)
|
||||
|
|
|
|||
|
|
@ -12,17 +12,14 @@
|
|||
package imagepuller
|
||||
|
||||
import (
|
||||
"context"
|
||||
goerror "errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/sirupsen/logrus"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
|
||||
chev1alpha1 "github.com/che-incubator/kubernetes-image-puller-operator/api/v1alpha1"
|
||||
|
|
@ -30,13 +27,9 @@ import (
|
|||
"github.com/eclipse-che/che-operator/pkg/common/constants"
|
||||
"github.com/eclipse-che/che-operator/pkg/common/utils"
|
||||
"github.com/eclipse-che/che-operator/pkg/deploy"
|
||||
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"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
)
|
||||
|
||||
|
|
@ -57,14 +50,12 @@ var (
|
|||
)
|
||||
|
||||
const (
|
||||
subscriptionName = "kubernetes-imagepuller-operator"
|
||||
operatorGroupName = "kubernetes-imagepuller-operator"
|
||||
packageName = "kubernetes-imagepuller-operator"
|
||||
componentName = "kubernetes-image-puller"
|
||||
imagePullerFinalizerName = "kubernetesimagepullers.finalizers.che.eclipse.org"
|
||||
defaultConfigMapName = "k8s-image-puller"
|
||||
defaultDeploymentName = "kubernetes-image-puller"
|
||||
defaultImagePullerImage = "quay.io/eclipse/kubernetes-image-puller:next"
|
||||
resourceName = "kubernetesimagepullers"
|
||||
finalizerName = "kubernetesimagepullers.finalizers.che.eclipse.org"
|
||||
|
||||
defaultConfigMapName = "k8s-image-puller"
|
||||
defaultDeploymentName = "kubernetes-image-puller"
|
||||
defaultImagePullerImage = "quay.io/eclipse/kubernetes-image-puller:next"
|
||||
)
|
||||
|
||||
type Images2Pull = map[string]string
|
||||
|
|
@ -79,44 +70,16 @@ func NewImagePuller() *ImagePuller {
|
|||
|
||||
func (ip *ImagePuller) Reconcile(ctx *chetypes.DeployContext) (reconcile.Result, bool, error) {
|
||||
if ctx.CheCluster.Spec.Components.ImagePuller.Enable {
|
||||
if foundPackagesAPI, foundOperatorsAPI, _, err := ip.discoverImagePullerApis(ctx); !foundPackagesAPI || !foundOperatorsAPI {
|
||||
if err != nil {
|
||||
return reconcile.Result{}, false, err
|
||||
}
|
||||
errorMsg := "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}, false, fmt.Errorf(errorMsg)
|
||||
}
|
||||
|
||||
if err := deploy.AppendFinalizer(ctx, imagePullerFinalizerName); err != nil {
|
||||
return reconcile.Result{}, false, err
|
||||
}
|
||||
|
||||
if done, err := ip.syncOperatorGroup(ctx); !done {
|
||||
return reconcile.Result{}, false, err
|
||||
}
|
||||
|
||||
if done, err := ip.syncSubscription(ctx); !done {
|
||||
return reconcile.Result{}, false, err
|
||||
}
|
||||
|
||||
if done, err := ip.syncDefaultImages(ctx); !done {
|
||||
return reconcile.Result{}, false, err
|
||||
}
|
||||
|
||||
// Wait for KubernetesImagePuller API
|
||||
if _, _, foundKubernetesImagePullerAPI, err := ip.discoverImagePullerApis(ctx); !foundKubernetesImagePullerAPI {
|
||||
if err != nil {
|
||||
return reconcile.Result{}, false, err
|
||||
}
|
||||
logrus.Infof("Waiting 15 seconds for kubernetesimagepullers.che.eclipse.org API")
|
||||
return reconcile.Result{RequeueAfter: 15 * time.Second}, false, nil
|
||||
if !utils.IsK8SResourceServed(ctx.ClusterAPI.DiscoveryClient, resourceName) {
|
||||
errMsg := "Kubernetes Image Puller is not installed, in order to enable the property admin should install the operator first"
|
||||
return reconcile.Result{}, false, fmt.Errorf(errMsg)
|
||||
}
|
||||
|
||||
if done, err := ip.syncKubernetesImagePuller(ctx); !done {
|
||||
return reconcile.Result{}, false, err
|
||||
}
|
||||
} else {
|
||||
if done, err := ip.uninstallImagePullerOperator(ctx); !done {
|
||||
if done, err := ip.uninstallImagePuller(ctx); !done {
|
||||
return reconcile.Result{}, false, err
|
||||
}
|
||||
}
|
||||
|
|
@ -124,204 +87,34 @@ func (ip *ImagePuller) Reconcile(ctx *chetypes.DeployContext) (reconcile.Result,
|
|||
}
|
||||
|
||||
func (ip *ImagePuller) Finalize(ctx *chetypes.DeployContext) bool {
|
||||
done, err := ip.uninstallImagePullerOperator(ctx)
|
||||
done, err := ip.uninstallImagePuller(ctx)
|
||||
if err != nil {
|
||||
log.Error(err, "Failed to uninstall KubernetesImagePuller")
|
||||
log.Error(err, "Failed to uninstall Kubernetes Image Puller")
|
||||
}
|
||||
return done
|
||||
}
|
||||
|
||||
// Uninstall the CSV, OperatorGroup, Subscription, KubernetesImagePuller, and update the CheCluster to remove
|
||||
// the image puller spec. Returns true if the CheCluster was updated
|
||||
func (ip *ImagePuller) uninstallImagePullerOperator(ctx *chetypes.DeployContext) (bool, error) {
|
||||
_, foundOperatorsAPI, foundKubernetesImagePullerAPI, err := ip.discoverImagePullerApis(ctx)
|
||||
if err != nil {
|
||||
func (ip *ImagePuller) uninstallImagePuller(ctx *chetypes.DeployContext) (bool, error) {
|
||||
// Keep it here for backward compatability
|
||||
if err := deploy.DeleteFinalizer(ctx, finalizerName); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if foundKubernetesImagePullerAPI {
|
||||
if utils.IsK8SResourceServed(ctx.ClusterAPI.DiscoveryClient, resourceName) {
|
||||
if done, err := deploy.DeleteByKeyWithClient(
|
||||
ctx.ClusterAPI.NonCachingClient,
|
||||
types.NamespacedName{Namespace: ctx.CheCluster.Namespace, Name: getImagePullerOperatorName(ctx)},
|
||||
types.NamespacedName{
|
||||
Namespace: ctx.CheCluster.Namespace,
|
||||
Name: getImagePullerCustomResourceName(ctx)},
|
||||
&chev1alpha1.KubernetesImagePuller{},
|
||||
); !done {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
if foundOperatorsAPI {
|
||||
// Delete the Subscription and ClusterServiceVersion
|
||||
subscription := &operatorsv1alpha1.Subscription{}
|
||||
if exists, err := deploy.GetWithClient(
|
||||
ctx.ClusterAPI.NonCachingClient,
|
||||
types.NamespacedName{Namespace: ctx.CheCluster.Namespace, Name: subscriptionName},
|
||||
subscription,
|
||||
); err != nil {
|
||||
return false, err
|
||||
} else if exists {
|
||||
if subscription.Status.InstalledCSV != "" {
|
||||
if done, err := deploy.DeleteByKeyWithClient(
|
||||
ctx.ClusterAPI.NonCachingClient,
|
||||
types.NamespacedName{Namespace: ctx.CheCluster.Namespace, Name: subscription.Status.InstalledCSV},
|
||||
&operatorsv1alpha1.ClusterServiceVersion{}); !done {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
if done, err := deploy.DeleteByKeyWithClient(
|
||||
ctx.ClusterAPI.NonCachingClient,
|
||||
types.NamespacedName{Namespace: ctx.CheCluster.Namespace, Name: subscriptionName},
|
||||
&operatorsv1alpha1.Subscription{}); !done {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the OperatorGroup
|
||||
if done, err := deploy.DeleteByKeyWithClient(
|
||||
ctx.ClusterAPI.NonCachingClient,
|
||||
types.NamespacedName{Namespace: ctx.CheCluster.Namespace, Name: operatorGroupName},
|
||||
&operatorsv1.OperatorGroup{},
|
||||
); !done {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := deploy.DeleteFinalizer(ctx, imagePullerFinalizerName); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// CheckNeededImagePullerApis check if the API server can discover the API groups
|
||||
// for packages.operators.coreos.com, operators.coreos.com, and che.eclipse.org.
|
||||
// Returns:
|
||||
// foundPackagesAPI - true if the server discovers the packages.operators.coreos.com API
|
||||
// foundOperatorsAPI - true if the server discovers the operators.coreos.com API
|
||||
// foundKubernetesImagePullerAPI - true if the server discovers the che.eclipse.org API
|
||||
// error - any error returned by the call to discoveryClient.ServerGroups()
|
||||
func (ip *ImagePuller) discoverImagePullerApis(ctx *chetypes.DeployContext) (bool, bool, bool, error) {
|
||||
groupList, resourcesList, err := ctx.ClusterAPI.DiscoveryClient.ServerGroupsAndResources()
|
||||
if err != nil {
|
||||
return false, false, false, err
|
||||
}
|
||||
foundPackagesAPI := false
|
||||
foundOperatorsAPI := false
|
||||
foundKubernetesImagePullerAPI := false
|
||||
for _, group := range groupList {
|
||||
if group.Name == packagesv1.SchemeGroupVersion.Group {
|
||||
foundPackagesAPI = true
|
||||
}
|
||||
if group.Name == operatorsv1alpha1.SchemeGroupVersion.Group {
|
||||
foundOperatorsAPI = true
|
||||
}
|
||||
}
|
||||
|
||||
for _, l := range resourcesList {
|
||||
for _, r := range l.APIResources {
|
||||
if l.GroupVersion == chev1alpha1.SchemeBuilder.GroupVersion.String() && r.Kind == "KubernetesImagePuller" {
|
||||
foundKubernetesImagePullerAPI = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return foundPackagesAPI, foundOperatorsAPI, foundKubernetesImagePullerAPI, nil
|
||||
}
|
||||
|
||||
func (ip *ImagePuller) syncDefaultImages(ctx *chetypes.DeployContext) (bool, error) {
|
||||
defaultImages := getDefaultImages()
|
||||
specImages := stringToImages(ctx.CheCluster.Spec.Components.ImagePuller.Spec.Images)
|
||||
|
||||
if len(specImages) == 0 {
|
||||
specImages = defaultImages
|
||||
} else {
|
||||
for specImageName, specImage := range specImages {
|
||||
for defaultImageName, defaultImage := range defaultImages {
|
||||
if specImageName == defaultImageName && specImage != defaultImage {
|
||||
specImages[specImageName] = defaultImage
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
specImagesAsString := imagesToString(specImages)
|
||||
if ctx.CheCluster.Spec.Components.ImagePuller.Spec.Images != specImagesAsString {
|
||||
ctx.CheCluster.Spec.Components.ImagePuller.Spec.Images = specImagesAsString
|
||||
err := deploy.UpdateCheCRSpec(ctx, "components.imagePuller.spec.images ", specImagesAsString)
|
||||
return err == nil, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (ip *ImagePuller) syncOperatorGroup(ctx *chetypes.DeployContext) (bool, error) {
|
||||
operatorGroupList := &operatorsv1.OperatorGroupList{}
|
||||
if err := ctx.ClusterAPI.NonCachingClient.List(context.TODO(), operatorGroupList, &client.ListOptions{Namespace: ctx.CheCluster.Namespace}); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if len(operatorGroupList.Items) != 0 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
operatorGroup := &operatorsv1.OperatorGroup{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "OperatorGroup",
|
||||
APIVersion: operatorsv1.SchemeGroupVersion.String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: operatorGroupName,
|
||||
Namespace: ctx.CheCluster.Namespace,
|
||||
Labels: map[string]string{
|
||||
constants.KubernetesPartOfLabelKey: constants.CheEclipseOrg,
|
||||
constants.KubernetesComponentLabelKey: componentName,
|
||||
constants.KubernetesManagedByLabelKey: deploy.GetManagedByLabel(),
|
||||
},
|
||||
},
|
||||
Spec: operatorsv1.OperatorGroupSpec{
|
||||
TargetNamespaces: []string{},
|
||||
},
|
||||
}
|
||||
|
||||
_, err := deploy.CreateIfNotExistsWithClient(ctx.ClusterAPI.NonCachingClient, ctx, operatorGroup)
|
||||
return err == nil, err
|
||||
}
|
||||
|
||||
func (ip *ImagePuller) syncSubscription(ctx *chetypes.DeployContext) (bool, error) {
|
||||
packageManifest := &packagesv1.PackageManifest{}
|
||||
if exists, err := deploy.GetWithClient(ctx.ClusterAPI.NonCachingClient, types.NamespacedName{Namespace: ctx.CheCluster.Namespace, Name: packageName}, packageManifest); !exists {
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return false, fmt.Errorf("there is no PackageManifest for the Kubernetes Image Puller Operator. Install the Operator Lifecycle Manager and the Community Operators Catalog")
|
||||
}
|
||||
|
||||
subscription := &operatorsv1alpha1.Subscription{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Subscription",
|
||||
APIVersion: operatorsv1alpha1.SchemeGroupVersion.String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: subscriptionName,
|
||||
Namespace: ctx.CheCluster.Namespace,
|
||||
Labels: map[string]string{
|
||||
constants.KubernetesPartOfLabelKey: constants.CheEclipseOrg,
|
||||
constants.KubernetesComponentLabelKey: componentName,
|
||||
constants.KubernetesManagedByLabelKey: deploy.GetManagedByLabel(),
|
||||
},
|
||||
},
|
||||
Spec: &operatorsv1alpha1.SubscriptionSpec{
|
||||
CatalogSource: packageManifest.Status.CatalogSource,
|
||||
CatalogSourceNamespace: packageManifest.Status.CatalogSourceNamespace,
|
||||
Channel: packageManifest.Status.DefaultChannel,
|
||||
InstallPlanApproval: operatorsv1alpha1.ApprovalAutomatic,
|
||||
Package: packageName,
|
||||
},
|
||||
}
|
||||
|
||||
_, err := deploy.CreateIfNotExistsWithClient(ctx.ClusterAPI.NonCachingClient, ctx, subscription)
|
||||
return err == nil, err
|
||||
}
|
||||
|
||||
func (ip *ImagePuller) syncKubernetesImagePuller(ctx *chetypes.DeployContext) (bool, error) {
|
||||
imagePuller := &chev1alpha1.KubernetesImagePuller{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
|
|
@ -329,15 +122,15 @@ func (ip *ImagePuller) syncKubernetesImagePuller(ctx *chetypes.DeployContext) (b
|
|||
Kind: "KubernetesImagePuller",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: getImagePullerOperatorName(ctx),
|
||||
Name: getImagePullerCustomResourceName(ctx),
|
||||
Namespace: ctx.CheCluster.Namespace,
|
||||
Labels: map[string]string{
|
||||
constants.KubernetesComponentLabelKey: constants.KubernetesImagePullerComponentName,
|
||||
constants.KubernetesPartOfLabelKey: constants.CheEclipseOrg,
|
||||
constants.KubernetesComponentLabelKey: componentName,
|
||||
constants.KubernetesManagedByLabelKey: deploy.GetManagedByLabel(),
|
||||
},
|
||||
},
|
||||
Spec: ctx.CheCluster.Spec.Components.ImagePuller.Spec,
|
||||
Spec: *ctx.CheCluster.Spec.Components.ImagePuller.Spec.DeepCopy(),
|
||||
}
|
||||
|
||||
// Set default values to avoid syncing object on every loop
|
||||
|
|
@ -345,11 +138,12 @@ func (ip *ImagePuller) syncKubernetesImagePuller(ctx *chetypes.DeployContext) (b
|
|||
imagePuller.Spec.ConfigMapName = utils.GetValue(imagePuller.Spec.ConfigMapName, defaultConfigMapName)
|
||||
imagePuller.Spec.DeploymentName = utils.GetValue(imagePuller.Spec.DeploymentName, defaultDeploymentName)
|
||||
imagePuller.Spec.ImagePullerImage = utils.GetValue(imagePuller.Spec.ImagePullerImage, defaultImagePullerImage)
|
||||
imagePuller.Spec.Images = utils.GetValue(imagePuller.Spec.Images, getDefaultImages())
|
||||
|
||||
return deploy.SyncWithClient(ctx.ClusterAPI.NonCachingClient, ctx, imagePuller, kubernetesImagePullerDiffOpts)
|
||||
}
|
||||
|
||||
func getImagePullerOperatorName(ctx *chetypes.DeployContext) string {
|
||||
func getImagePullerCustomResourceName(ctx *chetypes.DeployContext) string {
|
||||
return ctx.CheCluster.Name + "-image-puller"
|
||||
}
|
||||
|
||||
|
|
@ -371,31 +165,6 @@ func imagesToString(images Images2Pull) string {
|
|||
return imagesAsString
|
||||
}
|
||||
|
||||
// stringToImages returns a slice of ImageAndName structs from the provided semi-colon seperated string
|
||||
// of key value pairs
|
||||
func stringToImages(imagesString string) Images2Pull {
|
||||
currentImages := strings.Split(imagesString, ";")
|
||||
for i, image := range currentImages {
|
||||
currentImages[i] = strings.TrimSpace(image)
|
||||
}
|
||||
// Remove the last element, if empty
|
||||
if currentImages[len(currentImages)-1] == "" {
|
||||
currentImages = currentImages[:len(currentImages)-1]
|
||||
}
|
||||
|
||||
images := map[string]string{}
|
||||
for _, image := range currentImages {
|
||||
nameAndImage := strings.Split(image, "=")
|
||||
if len(nameAndImage) != 2 {
|
||||
logrus.Warnf("Malformed image name/tag: %s. Ignoring.", image)
|
||||
continue
|
||||
}
|
||||
images[nameAndImage[0]] = nameAndImage[1]
|
||||
}
|
||||
|
||||
return images
|
||||
}
|
||||
|
||||
// convertToRFC1123 converts input string to RFC 1123 format ([a-z0-9]([-a-z0-9]*[a-z0-9])?) max 63 characters, if possible
|
||||
func convertToRFC1123(str string) (string, error) {
|
||||
result := strings.ToLower(str)
|
||||
|
|
@ -424,7 +193,7 @@ func isRFC1123Char(ch byte) bool {
|
|||
}
|
||||
|
||||
// GetDefaultImages returns the current default images from the environment variables
|
||||
func getDefaultImages() Images2Pull {
|
||||
func getDefaultImages() string {
|
||||
images := map[string]string{}
|
||||
for _, pattern := range defaultImagePatterns {
|
||||
matches := utils.GetEnvsByRegExp(pattern)
|
||||
|
|
@ -436,5 +205,5 @@ func getDefaultImages() Images2Pull {
|
|||
images[match.Name[len("RELATED_IMAGE_"):]] = match.Value
|
||||
}
|
||||
}
|
||||
return images
|
||||
return imagesToString(images)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,29 +14,19 @@ package imagepuller
|
|||
import (
|
||||
"context"
|
||||
"os"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
chev1alpha1 "github.com/che-incubator/kubernetes-image-puller-operator/api/v1alpha1"
|
||||
"github.com/eclipse-che/che-operator/pkg/deploy"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
|
||||
"github.com/eclipse-che/che-operator/pkg/common/constants"
|
||||
"github.com/eclipse-che/che-operator/pkg/common/test"
|
||||
"github.com/eclipse-che/che-operator/pkg/common/utils"
|
||||
|
||||
chev2 "github.com/eclipse-che/che-operator/api/v2"
|
||||
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"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
|
@ -49,23 +39,12 @@ import (
|
|||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
namespace = "eclipse-che"
|
||||
csvName = "kubernetes-imagepuller-operator.v0.0.9"
|
||||
defaultImagePullerImages string
|
||||
valueTrue = true
|
||||
)
|
||||
|
||||
func TestImagePullerConfiguration(t *testing.T) {
|
||||
type testCase struct {
|
||||
name string
|
||||
initCR *chev2.CheCluster
|
||||
initObjects []runtime.Object
|
||||
expectedCR *chev2.CheCluster
|
||||
expectedOperatorGroup *operatorsv1.OperatorGroup
|
||||
expectedSubscription *operatorsv1alpha1.Subscription
|
||||
expectedImagePuller *chev1alpha1.KubernetesImagePuller
|
||||
shouldDelete bool
|
||||
name string
|
||||
cheCluster *chev2.CheCluster
|
||||
initObjects []runtime.Object
|
||||
expectedImagePuller *chev1alpha1.KubernetesImagePuller
|
||||
}
|
||||
|
||||
// unset RELATED_IMAGE environment variables, set them back after tests complete
|
||||
|
|
@ -79,144 +58,92 @@ func TestImagePullerConfiguration(t *testing.T) {
|
|||
|
||||
testCases := []testCase{
|
||||
{
|
||||
name: "image puller enabled, no operatorgroup, should create an operatorgroup",
|
||||
initCR: InitCheCRWithImagePullerEnabled(),
|
||||
initObjects: []runtime.Object{
|
||||
getPackageManifest(),
|
||||
},
|
||||
expectedOperatorGroup: getOperatorGroup(),
|
||||
name: "case #1: KubernetesImagePuller with defaults",
|
||||
cheCluster: InitCheCluster(chev2.ImagePuller{
|
||||
Enable: true,
|
||||
}),
|
||||
expectedImagePuller: InitImagePuller(chev1alpha1.KubernetesImagePullerSpec{
|
||||
DeploymentName: defaultDeploymentName,
|
||||
ConfigMapName: defaultConfigMapName,
|
||||
ImagePullerImage: defaultImagePullerImage,
|
||||
Images: getDefaultImages(),
|
||||
}),
|
||||
},
|
||||
{
|
||||
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: ExpectedCheCRWithImagePullerEnabled(),
|
||||
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: ExpectedCheCRWithImagePullerEnabled(),
|
||||
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, 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{
|
||||
Name: "eclipse-che-image-puller",
|
||||
Namespace: namespace,
|
||||
Labels: map[string]string{
|
||||
constants.KubernetesPartOfLabelKey: constants.CheEclipseOrg,
|
||||
constants.KubernetesComponentLabelKey: componentName,
|
||||
},
|
||||
OwnerReferences: []metav1.OwnerReference{
|
||||
{
|
||||
APIVersion: "org.eclipse.che/v2",
|
||||
Kind: "CheCluster",
|
||||
BlockOwnerDeletion: &valueTrue,
|
||||
Controller: &valueTrue,
|
||||
Name: "eclipse-che",
|
||||
},
|
||||
},
|
||||
},
|
||||
name: "case #2: KubernetesImagePuller with custom configuration",
|
||||
cheCluster: InitCheCluster(chev2.ImagePuller{
|
||||
Enable: true,
|
||||
Spec: chev1alpha1.KubernetesImagePullerSpec{
|
||||
ConfigMapName: "k8s-image-puller-trigger-update",
|
||||
DeploymentName: "kubernetes-image-puller-trigger-update",
|
||||
ImagePullerImage: "quay.io/eclipse/kubernetes-image-puller:next",
|
||||
},
|
||||
},
|
||||
ConfigMapName: "custom-config-map",
|
||||
ImagePullerImage: "custom-image",
|
||||
DeploymentName: "custom-deployment",
|
||||
Images: "image=image_url;",
|
||||
}}),
|
||||
expectedImagePuller: InitImagePuller(chev1alpha1.KubernetesImagePullerSpec{
|
||||
ConfigMapName: "custom-config-map",
|
||||
ImagePullerImage: "custom-image",
|
||||
DeploymentName: "custom-deployment",
|
||||
Images: "image=image_url;",
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "image puller already created, imagePuller disabled, should delete everything",
|
||||
initCR: InitCheCRWithImagePullerDisabled(),
|
||||
name: "case #3: KubernetesImagePuller already exists",
|
||||
cheCluster: InitCheCluster(chev2.ImagePuller{
|
||||
Enable: true,
|
||||
}),
|
||||
initObjects: []runtime.Object{
|
||||
getPackageManifest(),
|
||||
getOperatorGroup(),
|
||||
getSubscription(),
|
||||
getClusterServiceVersion(),
|
||||
getDefaultImagePuller(),
|
||||
InitImagePuller(chev1alpha1.KubernetesImagePullerSpec{
|
||||
DeploymentName: defaultDeploymentName,
|
||||
ConfigMapName: defaultConfigMapName,
|
||||
ImagePullerImage: defaultImagePullerImage,
|
||||
Images: getDefaultImages(),
|
||||
}),
|
||||
},
|
||||
expectedCR: InitCheCRWithImagePullerDisabled(),
|
||||
shouldDelete: true,
|
||||
expectedImagePuller: InitImagePuller(chev1alpha1.KubernetesImagePullerSpec{
|
||||
DeploymentName: defaultDeploymentName,
|
||||
ConfigMapName: defaultConfigMapName,
|
||||
ImagePullerImage: defaultImagePullerImage,
|
||||
Images: getDefaultImages(),
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "image puller already created, finalizer deleted",
|
||||
initCR: InitCheCRWithImagePullerFinalizerAndDeletionTimestamp(),
|
||||
name: "case #4: KubernetesImagePuller already exists and updated with custom configuration",
|
||||
cheCluster: InitCheCluster(chev2.ImagePuller{
|
||||
Enable: true,
|
||||
Spec: chev1alpha1.KubernetesImagePullerSpec{
|
||||
ConfigMapName: "custom-config-map",
|
||||
ImagePullerImage: "custom-image",
|
||||
DeploymentName: "custom-deployment",
|
||||
Images: "image=image_url;",
|
||||
}}),
|
||||
initObjects: []runtime.Object{
|
||||
getPackageManifest(),
|
||||
getOperatorGroup(),
|
||||
getSubscription(),
|
||||
getClusterServiceVersion(),
|
||||
getDefaultImagePuller(),
|
||||
InitImagePuller(chev1alpha1.KubernetesImagePullerSpec{
|
||||
DeploymentName: defaultDeploymentName,
|
||||
ConfigMapName: defaultConfigMapName,
|
||||
ImagePullerImage: defaultImagePullerImage,
|
||||
Images: getDefaultImages(),
|
||||
}),
|
||||
},
|
||||
expectedImagePuller: InitImagePuller(chev1alpha1.KubernetesImagePullerSpec{
|
||||
ConfigMapName: "custom-config-map",
|
||||
ImagePullerImage: "custom-image",
|
||||
DeploymentName: "custom-deployment",
|
||||
Images: "image=image_url;",
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "case #5: Delete KubernetesImagePuller",
|
||||
cheCluster: InitCheCluster(chev2.ImagePuller{
|
||||
Enable: false,
|
||||
}),
|
||||
initObjects: []runtime.Object{
|
||||
InitImagePuller(chev1alpha1.KubernetesImagePullerSpec{
|
||||
DeploymentName: defaultDeploymentName,
|
||||
ConfigMapName: defaultConfigMapName,
|
||||
ImagePullerImage: defaultImagePullerImage,
|
||||
Images: getDefaultImages(),
|
||||
}),
|
||||
},
|
||||
shouldDelete: true,
|
||||
expectedCR: nil,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -224,208 +151,51 @@ func TestImagePullerConfiguration(t *testing.T) {
|
|||
t.Run(testCase.name, func(t *testing.T) {
|
||||
logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true)))
|
||||
|
||||
deployContext := test.GetDeployContext(testCase.initCR, []runtime.Object{})
|
||||
|
||||
chev2.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.NonCachingClient.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"},
|
||||
},
|
||||
},
|
||||
ctx := test.GetDeployContext(testCase.cheCluster, []runtime.Object{})
|
||||
ctx.ClusterAPI.DiscoveryClient.(*fakeDiscovery.FakeDiscovery).Fake.Resources = []*metav1.APIResourceList{
|
||||
{
|
||||
GroupVersion: "che.eclipse.org/v1alpha1",
|
||||
APIResources: []metav1.APIResource{
|
||||
{Kind: "KubernetesImagePuller"},
|
||||
{
|
||||
Name: resourceName,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var err error
|
||||
for _, obj := range testCase.initObjects {
|
||||
err := ctx.ClusterAPI.Client.Create(context.TODO(), obj.(client.Object))
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
ip := NewImagePuller()
|
||||
if testCase.shouldDelete {
|
||||
if done, _ := ip.uninstallImagePullerOperator(deployContext); !done {
|
||||
t.Fatal("Error reconciling: failed to delete Image Puller")
|
||||
}
|
||||
} else {
|
||||
_, _, err = ip.Reconcile(deployContext)
|
||||
if err != nil {
|
||||
t.Fatalf("Error reconciling: %v", err)
|
||||
}
|
||||
}
|
||||
_, _, err := ip.Reconcile(ctx)
|
||||
assert.NoError(t, err)
|
||||
|
||||
if testCase.expectedOperatorGroup != nil {
|
||||
gotOperatorGroup := &operatorsv1.OperatorGroup{}
|
||||
err := deployContext.ClusterAPI.NonCachingClient.Get(context.TODO(), types.NamespacedName{Namespace: testCase.expectedOperatorGroup.Namespace, Name: testCase.expectedOperatorGroup.Name}, gotOperatorGroup)
|
||||
if err != nil {
|
||||
t.Errorf("Error getting OperatorGroup: %v", err)
|
||||
}
|
||||
assert.Empty(t, gotOperatorGroup.Spec.TargetNamespaces)
|
||||
}
|
||||
if testCase.expectedSubscription != nil {
|
||||
gotSubscription := &operatorsv1alpha1.Subscription{}
|
||||
err := deployContext.ClusterAPI.NonCachingClient.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 := &chev2.CheCluster{}
|
||||
err = deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: "eclipse-che"}, gotCR)
|
||||
if err != nil {
|
||||
t.Errorf("Error getting CheCluster: %v", err)
|
||||
}
|
||||
actualImagePuller := &chev1alpha1.KubernetesImagePuller{}
|
||||
err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Namespace: "eclipse-che", Name: "eclipse-che-image-puller"}, actualImagePuller)
|
||||
if testCase.cheCluster.Spec.Components.ImagePuller.Enable {
|
||||
assert.NoError(t, err)
|
||||
|
||||
diff := cmp.Diff(testCase.expectedCR, gotCR, cmp.Options{cmpopts.IgnoreFields(chev2.CheCluster{}, "TypeMeta", "ObjectMeta")})
|
||||
if diff != "" {
|
||||
t.Errorf("Expected CR and CR returned from API server are different (-want +got): %s", diff)
|
||||
}
|
||||
}
|
||||
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.KubernetesImagePuller{}, "ObjectMeta"))
|
||||
diff := cmp.Diff(
|
||||
testCase.expectedImagePuller,
|
||||
actualImagePuller,
|
||||
cmpopts.IgnoreFields(metav1.ObjectMeta{}, "ResourceVersion", "OwnerReferences"))
|
||||
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 := &chev2.CheCluster{}
|
||||
err = deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: "eclipse-che"}, 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: "eclipse-che-image-puller"}, imagePuller)
|
||||
if err == nil || !errors.IsNotFound(err) {
|
||||
t.Fatalf("Should not have found KubernetesImagePuller: %v", err)
|
||||
}
|
||||
|
||||
clusterServiceVersion := &operatorsv1alpha1.ClusterServiceVersion{}
|
||||
err = deployContext.ClusterAPI.NonCachingClient.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.NonCachingClient.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.NonCachingClient.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)
|
||||
}
|
||||
} else {
|
||||
assert.True(t, errors.IsNotFound(err))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyncImages(t *testing.T) {
|
||||
func TestDefaultImages(t *testing.T) {
|
||||
type testcase struct {
|
||||
name string
|
||||
env map[string]string
|
||||
expectedImagesAsString string
|
||||
}
|
||||
|
||||
// unset RELATED_IMAGE environment variables, set them back after tests complete
|
||||
matches := utils.GetEnvsByRegExp("^RELATED_IMAGE_.*")
|
||||
for _, match := range matches {
|
||||
if originalValue, exists := os.LookupEnv(match.Name); exists {
|
||||
os.Unsetenv(match.Name)
|
||||
defer os.Setenv(match.Name, originalValue)
|
||||
}
|
||||
}
|
||||
|
||||
testCases := []testcase{
|
||||
{
|
||||
name: "detect devfile registry images",
|
||||
env: map[string]string{
|
||||
"RELATED_IMAGE_universal_developer_image_devfile_registry_image_1": "quay.io/devfile/universal-developer-image@sha256:test1",
|
||||
"RELATED_IMAGE_universal_developer_image_devfile_registry_image_2": "quay.io/devfile/universal-developer-image@sha256:test2",
|
||||
},
|
||||
expectedImagesAsString: "universal-developer-image-devfile-registry-image-1=quay.io/devfile/universal-developer-image@sha256:test1;universal-developer-image-devfile-registry-image-2=quay.io/devfile/universal-developer-image@sha256:test2;",
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
ctx := test.GetDeployContext(nil, []runtime.Object{})
|
||||
|
||||
for k, v := range testCase.env {
|
||||
os.Setenv(k, v)
|
||||
defer os.Unsetenv(k)
|
||||
}
|
||||
|
||||
ip := NewImagePuller()
|
||||
done, err := ip.syncDefaultImages(ctx)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, done)
|
||||
assert.Equal(t, testCase.expectedImagesAsString, ctx.CheCluster.Spec.Components.ImagePuller.Spec.Images)
|
||||
|
||||
// sync twice to ensure images are the same
|
||||
done, err = ip.syncDefaultImages(ctx)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, done)
|
||||
assert.Equal(t, testCase.expectedImagesAsString, ctx.CheCluster.Spec.Components.ImagePuller.Spec.Images)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnvVars(t *testing.T) {
|
||||
type testcase struct {
|
||||
name string
|
||||
env map[string]string
|
||||
expected Images2Pull
|
||||
expected string
|
||||
expectedImagesAsString string
|
||||
}
|
||||
|
||||
|
|
@ -440,46 +210,20 @@ func TestEnvVars(t *testing.T) {
|
|||
|
||||
cases := []testcase{
|
||||
{
|
||||
name: "detect theia images",
|
||||
name: "case #1",
|
||||
env: map[string]string{
|
||||
"RELATED_IMAGE_che_theia_plugin_registry_image_IBZWQYJ": "quay.io/eclipse/che-theia",
|
||||
"RELATED_IMAGE_che_theia_endpoint_runtime_binary_plugin_registry_image_IBZWQYJ": "quay.io/eclipse/che-theia-endpoint-runtime-binary",
|
||||
},
|
||||
expected: map[string]string{
|
||||
"che_theia_plugin_registry_image_IBZWQYJ": "quay.io/eclipse/che-theia",
|
||||
"che_theia_endpoint_runtime_binary_plugin_registry_image_IBZWQYJ": "quay.io/eclipse/che-theia-endpoint-runtime-binary",
|
||||
},
|
||||
expected: "che-theia-endpoint-runtime-binary-plugin-registry-image-ibzwqyj=quay.io/eclipse/che-theia-endpoint-runtime-binary;che-theia-plugin-registry-image-ibzwqyj=quay.io/eclipse/che-theia;",
|
||||
},
|
||||
{
|
||||
name: "detect machine exec image",
|
||||
name: "case #2",
|
||||
env: map[string]string{
|
||||
"RELATED_IMAGE_che_machine_exec_plugin_registry_image_IBZWQYJ": "quay.io/eclipse/che-machine-exec",
|
||||
"RELATED_IMAGE_codeready_workspaces_machineexec_plugin_registry_image_GIXDCMQK": "registry.redhat.io/codeready-workspaces/machineexec-rhel8",
|
||||
},
|
||||
expected: map[string]string{
|
||||
"che_machine_exec_plugin_registry_image_IBZWQYJ": "quay.io/eclipse/che-machine-exec",
|
||||
"codeready_workspaces_machineexec_plugin_registry_image_GIXDCMQK": "registry.redhat.io/codeready-workspaces/machineexec-rhel8",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "detect plugin registry images",
|
||||
env: map[string]string{
|
||||
"RELATED_IMAGE_che_openshift_plugin_registry_image_IBZWQYJ": "index.docker.io/dirigiblelabs/dirigible-openshift",
|
||||
"RELATED_IMAGE_codeready_workspaces_plugin_openshift_plugin_registry_image_GIXDCMQK": "registry.redhat.io/codeready-workspaces/plugin-openshift-rhel8",
|
||||
},
|
||||
expected: map[string]string{
|
||||
"che_openshift_plugin_registry_image_IBZWQYJ": "index.docker.io/dirigiblelabs/dirigible-openshift",
|
||||
"codeready_workspaces_plugin_openshift_plugin_registry_image_GIXDCMQK": "registry.redhat.io/codeready-workspaces/plugin-openshift-rhel8",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "detect devfile registry images",
|
||||
env: map[string]string{
|
||||
"RELATED_IMAGE_universal_developer_image_devfile_registry_image_OVRGSOBNGBSTCOBZMQ4Q____": "quay.io/devfile/universal-developer-image:ubi8-38da5c2",
|
||||
},
|
||||
expected: map[string]string{
|
||||
"universal_developer_image_devfile_registry_image_OVRGSOBNGBSTCOBZMQ4Q____": "quay.io/devfile/universal-developer-image:ubi8-38da5c2",
|
||||
},
|
||||
expected: "che-machine-exec-plugin-registry-image-ibzwqyj=quay.io/eclipse/che-machine-exec;codeready-workspaces-machineexec-plugin-registry-image-gixdcmqk=registry.redhat.io/codeready-workspaces/machineexec-rhel8;",
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -497,140 +241,11 @@ func TestEnvVars(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func InitCheCRWithImagePullerEnabled() *chev2.CheCluster {
|
||||
func InitCheCluster(imagePuller chev2.ImagePuller) *chev2.CheCluster {
|
||||
return &chev2.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "eclipse-che",
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: chev2.CheClusterSpec{
|
||||
Components: chev2.CheClusterComponents{
|
||||
ImagePuller: chev2.ImagePuller{
|
||||
Enable: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func InitCheCRWithImagePullerFinalizer() *chev2.CheCluster {
|
||||
return &chev2.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "eclipse-che",
|
||||
Namespace: namespace,
|
||||
Finalizers: []string{
|
||||
"kubernetesimagepullers.finalizers.che.eclipse.org",
|
||||
},
|
||||
},
|
||||
Spec: chev2.CheClusterSpec{
|
||||
Components: chev2.CheClusterComponents{
|
||||
ImagePuller: chev2.ImagePuller{
|
||||
Enable: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func InitCheCRWithImagePullerFinalizerAndDeletionTimestamp() *chev2.CheCluster {
|
||||
return &chev2.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "eclipse-che",
|
||||
Namespace: namespace,
|
||||
Finalizers: []string{
|
||||
"kubernetesimagepullers.finalizers.che.eclipse.org",
|
||||
},
|
||||
DeletionTimestamp: &metav1.Time{Time: time.Unix(1, 0)},
|
||||
},
|
||||
Spec: chev2.CheClusterSpec{
|
||||
Components: chev2.CheClusterComponents{
|
||||
ImagePuller: chev2.ImagePuller{
|
||||
Enable: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func ExpectedCheCRWithImagePullerEnabled() *chev2.CheCluster {
|
||||
return &chev2.CheCluster{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "org.eclipse.che/v2",
|
||||
Kind: "CheCluster",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "eclipse-che",
|
||||
Namespace: namespace,
|
||||
Finalizers: []string{
|
||||
"kubernetesimagepullers.finalizers.che.eclipse.org",
|
||||
},
|
||||
},
|
||||
Spec: chev2.CheClusterSpec{
|
||||
Components: chev2.CheClusterComponents{
|
||||
ImagePuller: chev2.ImagePuller{
|
||||
Enable: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func InitCheCRWithImagePullerDisabled() *chev2.CheCluster {
|
||||
return &chev2.CheCluster{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "org.eclipse.che/v2",
|
||||
Kind: "CheCluster",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "eclipse-che",
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: chev2.CheClusterSpec{
|
||||
Components: chev2.CheClusterComponents{
|
||||
ImagePuller: chev2.ImagePuller{
|
||||
Enable: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func InitCheCRWithImagePullerEnabledAndDefaultValuesSet() *chev2.CheCluster {
|
||||
return &chev2.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "eclipse-che",
|
||||
Namespace: namespace,
|
||||
Finalizers: []string{
|
||||
"kubernetesimagepullers.finalizers.che.eclipse.org",
|
||||
},
|
||||
},
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "org.eclipse.che/v2",
|
||||
Kind: "CheCluster",
|
||||
},
|
||||
|
||||
Spec: chev2.CheClusterSpec{
|
||||
Components: chev2.CheClusterComponents{
|
||||
ImagePuller: chev2.ImagePuller{
|
||||
Enable: true,
|
||||
Spec: chev1alpha1.KubernetesImagePullerSpec{
|
||||
DeploymentName: "deployment",
|
||||
ConfigMapName: "configmap",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func InitCheCRWithImagePullerEnabledAndImagesSet(images string) *chev2.CheCluster {
|
||||
return &chev2.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "eclipse-che",
|
||||
Namespace: namespace,
|
||||
Finalizers: []string{
|
||||
"kubernetesimagepullers.finalizers.che.eclipse.org",
|
||||
},
|
||||
Namespace: "eclipse-che",
|
||||
},
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "org.eclipse.che/v2",
|
||||
|
|
@ -638,170 +253,27 @@ func InitCheCRWithImagePullerEnabledAndImagesSet(images string) *chev2.CheCluste
|
|||
},
|
||||
Spec: chev2.CheClusterSpec{
|
||||
Components: chev2.CheClusterComponents{
|
||||
ImagePuller: chev2.ImagePuller{
|
||||
Enable: true,
|
||||
Spec: chev1alpha1.KubernetesImagePullerSpec{
|
||||
DeploymentName: "deployment",
|
||||
ConfigMapName: "configmap",
|
||||
Images: images,
|
||||
},
|
||||
},
|
||||
ImagePuller: imagePuller,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func InitCheCRWithImagePullerEnabledAndNewValuesSet() *chev2.CheCluster {
|
||||
return &chev2.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "eclipse-che",
|
||||
Namespace: namespace,
|
||||
Finalizers: []string{
|
||||
"kubernetesimagepullers.finalizers.che.eclipse.org",
|
||||
},
|
||||
},
|
||||
Spec: chev2.CheClusterSpec{
|
||||
Components: chev2.CheClusterComponents{
|
||||
ImagePuller: chev2.ImagePuller{
|
||||
Enable: true,
|
||||
Spec: chev1alpha1.KubernetesImagePullerSpec{
|
||||
DeploymentName: "kubernetes-image-puller-trigger-update",
|
||||
ConfigMapName: "k8s-image-puller-trigger-update",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type ImagePullerOptions struct {
|
||||
SpecImages string
|
||||
ObjectMetaResourceVersion string
|
||||
}
|
||||
|
||||
func InitImagePuller(options ImagePullerOptions) *chev1alpha1.KubernetesImagePuller {
|
||||
func InitImagePuller(kubernetesImagePullerSpec chev1alpha1.KubernetesImagePullerSpec) *chev1alpha1.KubernetesImagePuller {
|
||||
return &chev1alpha1.KubernetesImagePuller{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "che.eclipse.org/v1alpha1",
|
||||
Kind: "KubernetesImagePuller",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "eclipse-che-image-puller",
|
||||
Namespace: namespace,
|
||||
ResourceVersion: options.ObjectMetaResourceVersion,
|
||||
Labels: map[string]string{
|
||||
constants.KubernetesPartOfLabelKey: constants.CheEclipseOrg,
|
||||
constants.KubernetesComponentLabelKey: componentName,
|
||||
},
|
||||
OwnerReferences: []metav1.OwnerReference{
|
||||
{
|
||||
APIVersion: "org.eclipse.che/v2",
|
||||
Kind: "CheCluster",
|
||||
Controller: &valueTrue,
|
||||
BlockOwnerDeletion: &valueTrue,
|
||||
Name: "eclipse-che",
|
||||
},
|
||||
},
|
||||
},
|
||||
Spec: chev1alpha1.KubernetesImagePullerSpec{
|
||||
DeploymentName: "deployment",
|
||||
ConfigMapName: "configmap",
|
||||
Images: options.SpecImages,
|
||||
ImagePullerImage: "quay.io/eclipse/kubernetes-image-puller:next",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getDefaultImagePuller() *chev1alpha1.KubernetesImagePuller {
|
||||
return &chev1alpha1.KubernetesImagePuller{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "che.eclipse.org/v1alpha1",
|
||||
APIVersion: chev1alpha1.GroupVersion.String(),
|
||||
Kind: "KubernetesImagePuller",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "eclipse-che-image-puller",
|
||||
Namespace: namespace,
|
||||
Namespace: "eclipse-che",
|
||||
Labels: map[string]string{
|
||||
constants.KubernetesComponentLabelKey: constants.KubernetesImagePullerComponentName,
|
||||
constants.KubernetesPartOfLabelKey: constants.CheEclipseOrg,
|
||||
constants.KubernetesComponentLabelKey: componentName,
|
||||
},
|
||||
OwnerReferences: []metav1.OwnerReference{
|
||||
{
|
||||
APIVersion: "org.eclipse.che/v2",
|
||||
Kind: "CheCluster",
|
||||
Controller: &valueTrue,
|
||||
BlockOwnerDeletion: &valueTrue,
|
||||
Name: "eclipse-che",
|
||||
},
|
||||
constants.KubernetesManagedByLabelKey: deploy.GetManagedByLabel(),
|
||||
},
|
||||
},
|
||||
Spec: chev1alpha1.KubernetesImagePullerSpec{
|
||||
DeploymentName: "deployment",
|
||||
ConfigMapName: "configmap",
|
||||
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{},
|
||||
},
|
||||
}
|
||||
}
|
||||
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",
|
||||
},
|
||||
Status: operatorsv1alpha1.SubscriptionStatus{
|
||||
InstalledCSV: csvName,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getClusterServiceVersion() *operatorsv1alpha1.ClusterServiceVersion {
|
||||
return &operatorsv1alpha1.ClusterServiceVersion{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: namespace,
|
||||
Name: csvName,
|
||||
},
|
||||
Spec: kubernetesImagePullerSpec,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue