From 355de0a0127432273a4e493d92577d327489615d Mon Sep 17 00:00:00 2001 From: Aravind Ramalingam <60027164+pokearu@users.noreply.github.com> Date: Mon, 22 Apr 2024 14:34:11 -0700 Subject: [PATCH] =?UTF-8?q?=F0=9F=8D=92=20Cherry=20pick=20packages=20with?= =?UTF-8?q?=20registry=20mirror=20fixes=20(#8029)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Adding a helm login to packages reconcile flow for workload clusters (#7971) * Adding permissions for controller packages helm upgrade (#8012) * Adding namespaces read permissions to eksa controller (#8017) --- config/manifest/eksa-components.yaml | 3 + config/rbac/role.yaml | 3 + controllers/cluster_controller.go | 4 +- controllers/factory.go | 2 +- pkg/curatedpackages/mocks/installer.go | 14 ++ .../packagecontrollerclient.go | 20 ++- .../packagecontrollerclient_test.go | 139 ++++++++++++++++++ 7 files changed, 179 insertions(+), 6 deletions(-) diff --git a/config/manifest/eksa-components.yaml b/config/manifest/eksa-components.yaml index 735a614f55cc..0e1ebe7959d3 100644 --- a/config/manifest/eksa-components.yaml +++ b/config/manifest/eksa-components.yaml @@ -7109,6 +7109,8 @@ rules: verbs: - create - delete + - get + - list - apiGroups: - "" resources: @@ -7134,6 +7136,7 @@ rules: - delete - get - list + - patch - update - watch - apiGroups: diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index f5b81ea51e36..acd2ae7f0898 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -29,6 +29,8 @@ rules: verbs: - create - delete + - get + - list - apiGroups: - "" resources: @@ -54,6 +56,7 @@ rules: - delete - get - list + - patch - update - watch - apiGroups: diff --git a/controllers/cluster_controller.go b/controllers/cluster_controller.go index 53f01f9e2255..953a62d1766b 100644 --- a/controllers/cluster_controller.go +++ b/controllers/cluster_controller.go @@ -171,9 +171,9 @@ func (r *ClusterReconciler) SetupWithManager(mgr ctrl.Manager, log logr.Logger) } // +kubebuilder:rbac:groups="",resources=events,verbs=create;patch;update -// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;delete;update +// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;delete;update;patch // +kubebuilder:rbac:groups="",namespace=eksa-system,resources=secrets,verbs=patch;update -// +kubebuilder:rbac:groups="",resources=namespaces,verbs=create;delete +// +kubebuilder:rbac:groups="",resources=namespaces,verbs=get;list;create;delete // +kubebuilder:rbac:groups="",resources=nodes,verbs=list // +kubebuilder:rbac:groups=addons.cluster.x-k8s.io,resources=clusterresourcesets,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=anywhere.eks.amazonaws.com,resources=clusters;gitopsconfigs;snowmachineconfigs;snowdatacenterconfigs;snowippools;vspheredatacenterconfigs;vspheremachineconfigs;dockerdatacenterconfigs;tinkerbellmachineconfigs;tinkerbelltemplateconfigs;tinkerbelldatacenterconfigs;cloudstackdatacenterconfigs;cloudstackmachineconfigs;nutanixdatacenterconfigs;nutanixmachineconfigs;awsiamconfigs;oidcconfigs;awsiamconfigs;fluxconfigs,verbs=get;list;watch;update;patch diff --git a/controllers/factory.go b/controllers/factory.go index 517c54b511af..35725e6aa6bf 100644 --- a/controllers/factory.go +++ b/controllers/factory.go @@ -561,7 +561,7 @@ func (f *Factory) withAWSIamConfigReconciler() *Factory { } func (f *Factory) withPackageControllerClient() *Factory { - f.dependencyFactory.WithHelm().WithKubectl() + f.dependencyFactory.WithHelm(helm.WithInsecure()).WithKubectl() f.buildSteps = append(f.buildSteps, func(ctx context.Context) error { if f.packageControllerClient != nil { diff --git a/pkg/curatedpackages/mocks/installer.go b/pkg/curatedpackages/mocks/installer.go index 7dbb146c16d9..fa67f8b523f1 100644 --- a/pkg/curatedpackages/mocks/installer.go +++ b/pkg/curatedpackages/mocks/installer.go @@ -176,6 +176,20 @@ func (mr *MockChartManagerMockRecorder) InstallChart(ctx, chart, ociURI, version return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InstallChart", reflect.TypeOf((*MockChartManager)(nil).InstallChart), ctx, chart, ociURI, version, kubeconfigFilePath, namespace, valueFilePath, skipCRDs, values) } +// RegistryLogin mocks base method. +func (m *MockChartManager) RegistryLogin(ctx context.Context, registry, username, password string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RegistryLogin", ctx, registry, username, password) + ret0, _ := ret[0].(error) + return ret0 +} + +// RegistryLogin indicates an expected call of RegistryLogin. +func (mr *MockChartManagerMockRecorder) RegistryLogin(ctx, registry, username, password interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegistryLogin", reflect.TypeOf((*MockChartManager)(nil).RegistryLogin), ctx, registry, username, password) +} + // MockKubeDeleter is a mock of KubeDeleter interface. type MockKubeDeleter struct { ctrl *gomock.Controller diff --git a/pkg/curatedpackages/packagecontrollerclient.go b/pkg/curatedpackages/packagecontrollerclient.go index 7188e474fe3f..e0fb2d28832a 100644 --- a/pkg/curatedpackages/packagecontrollerclient.go +++ b/pkg/curatedpackages/packagecontrollerclient.go @@ -95,6 +95,7 @@ type ChartUninstaller interface { type ChartManager interface { ChartInstaller ChartUninstaller + RegistryLogin(ctx context.Context, registry, username, password string) error } // NewPackageControllerClientFullLifecycle creates a PackageControllerClient @@ -307,9 +308,11 @@ func (pc *PackageControllerClient) generateHelmOverrideValues() ([]byte, error) endpoint, username, password, caCertContent, insecureSkipVerify := "", "", "", "", "false" if pc.registryMirror != nil { endpoint = pc.registryMirror.BaseRegistry - username, password, err = config.ReadCredentials() - if err != nil { - return []byte{}, err + if pc.registryMirror.Auth { + username, password, err = config.ReadCredentials() + if err != nil { + return []byte{}, err + } } caCertContent = pc.registryMirror.CACertContent if pc.registryMirror.InsecureSkipVerify { @@ -492,6 +495,17 @@ func (pc *PackageControllerClient) Reconcile(ctx context.Context, logger logr.Lo registry := registrymirror.FromCluster(cluster) + if registry != nil && registry.Auth { + rUsername, rPassword, err := config.ReadCredentialsFromSecret(ctx, client) + if err != nil { + return err + } + + if err := pc.chartManager.RegistryLogin(ctx, registry.BaseRegistry, rUsername, rPassword); err != nil { + return err + } + } + // No Kubeconfig is passed. This is intentional. The helm executable will // get that configuration from its environment. if err := pc.EnableFullLifecycle(ctx, logger, cluster.Name, "", image, registry, diff --git a/pkg/curatedpackages/packagecontrollerclient_test.go b/pkg/curatedpackages/packagecontrollerclient_test.go index 2af94ff9bc93..d31d3a4d6f35 100644 --- a/pkg/curatedpackages/packagecontrollerclient_test.go +++ b/pkg/curatedpackages/packagecontrollerclient_test.go @@ -1387,6 +1387,145 @@ func TestReconcile(s *testing.T) { t.Errorf("expected packages client error, got %s", err) } }) + + s.Run("golden path with registry mirror", func(t *testing.T) { + ctx := context.Background() + log := testr.New(t) + cluster := newReconcileTestCluster() + ctrl := gomock.NewController(t) + k := mocks.NewMockKubectlRunner(ctrl) + cm := mocks.NewMockChartManager(ctrl) + bundles := createBundle(cluster) + bundles.Spec.VersionsBundles[0].KubeVersion = string(cluster.Spec.KubernetesVersion) + bundles.ObjectMeta.Name = cluster.Spec.BundlesRef.Name + bundles.ObjectMeta.Namespace = cluster.Spec.BundlesRef.Namespace + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: constants.EksaSystemNamespace, + Name: cluster.Name + "-kubeconfig", + }, + } + registrySecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: constants.EksaSystemNamespace, + Name: "registry-credentials", + }, + } + eksaRelease := createEKSARelease(cluster, bundles) + cluster.Spec.BundlesRef = nil + cluster.Spec.RegistryMirrorConfiguration = &anywherev1.RegistryMirrorConfiguration{ + Endpoint: "1.2.3.4", + Port: "443", + Authenticate: true, + OCINamespaces: []anywherev1.OCINamespace{ + { + Namespace: "ecr-public", + Registry: "public.ecr.aws", + }, + }, + } + t.Setenv("REGISTRY_USERNAME", "username") + t.Setenv("REGISTRY_PASSWORD", "password") + + objs := []runtime.Object{cluster, bundles, secret, eksaRelease, registrySecret} + fakeClient := fake.NewClientBuilder().WithRuntimeObjects(objs...).Build() + cm.EXPECT().RegistryLogin(ctx, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) + cm.EXPECT().InstallChart(ctx, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) + + pcc := curatedpackages.NewPackageControllerClientFullLifecycle(log, cm, k, nil) + err := pcc.Reconcile(ctx, log, fakeClient, cluster) + if err != nil { + t.Errorf("expected nil error, got %s", err) + } + }) + + s.Run("registry mirror helm login fails", func(t *testing.T) { + ctx := context.Background() + log := testr.New(t) + cluster := newReconcileTestCluster() + ctrl := gomock.NewController(t) + k := mocks.NewMockKubectlRunner(ctrl) + cm := mocks.NewMockChartManager(ctrl) + bundles := createBundle(cluster) + bundles.Spec.VersionsBundles[0].KubeVersion = string(cluster.Spec.KubernetesVersion) + bundles.ObjectMeta.Name = cluster.Spec.BundlesRef.Name + bundles.ObjectMeta.Namespace = cluster.Spec.BundlesRef.Namespace + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: constants.EksaSystemNamespace, + Name: cluster.Name + "-kubeconfig", + }, + } + registrySecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: constants.EksaSystemNamespace, + Name: "registry-credentials", + }, + } + eksaRelease := createEKSARelease(cluster, bundles) + cluster.Spec.BundlesRef = nil + cluster.Spec.RegistryMirrorConfiguration = &anywherev1.RegistryMirrorConfiguration{ + Endpoint: "1.2.3.4", + Port: "443", + Authenticate: true, + OCINamespaces: []anywherev1.OCINamespace{ + { + Namespace: "ecr-public", + Registry: "public.ecr.aws", + }, + }, + } + t.Setenv("REGISTRY_USERNAME", "username") + t.Setenv("REGISTRY_PASSWORD", "password") + + objs := []runtime.Object{cluster, bundles, secret, eksaRelease, registrySecret} + fakeClient := fake.NewClientBuilder().WithRuntimeObjects(objs...).Build() + cm.EXPECT().RegistryLogin(ctx, gomock.Any(), gomock.Any(), gomock.Any()).Return(fmt.Errorf("login error")) + pcc := curatedpackages.NewPackageControllerClientFullLifecycle(log, cm, k, nil) + err := pcc.Reconcile(ctx, log, fakeClient, cluster) + if err == nil { + t.Errorf("expected error, got %s", err) + } + }) + + s.Run("registry mirror secret not found error", func(t *testing.T) { + ctx := context.Background() + log := testr.New(t) + cluster := newReconcileTestCluster() + ctrl := gomock.NewController(t) + k := mocks.NewMockKubectlRunner(ctrl) + cm := mocks.NewMockChartManager(ctrl) + bundles := createBundle(cluster) + bundles.Spec.VersionsBundles[0].KubeVersion = string(cluster.Spec.KubernetesVersion) + bundles.ObjectMeta.Name = cluster.Spec.BundlesRef.Name + bundles.ObjectMeta.Namespace = cluster.Spec.BundlesRef.Namespace + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: constants.EksaSystemNamespace, + Name: cluster.Name + "-kubeconfig", + }, + } + eksaRelease := createEKSARelease(cluster, bundles) + cluster.Spec.BundlesRef = nil + cluster.Spec.RegistryMirrorConfiguration = &anywherev1.RegistryMirrorConfiguration{ + Endpoint: "1.2.3.4", + Port: "443", + Authenticate: true, + OCINamespaces: []anywherev1.OCINamespace{ + { + Namespace: "ecr-public", + Registry: "public.ecr.aws", + }, + }, + } + objs := []runtime.Object{cluster, bundles, secret, eksaRelease} + fakeClient := fake.NewClientBuilder().WithRuntimeObjects(objs...).Build() + pcc := curatedpackages.NewPackageControllerClientFullLifecycle(log, cm, k, nil) + err := pcc.Reconcile(ctx, log, fakeClient, cluster) + if err == nil || !strings.Contains(err.Error(), "not found") { + t.Errorf("expected error, got %s", err) + } + }) } func newReconcileTestCluster() *anywherev1.Cluster {