package bundle import ( "context" "fmt" "os" v1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" api "github.com/aws/eks-anywhere-packages/api/v1alpha1" auth "github.com/aws/eks-anywhere-packages/pkg/authenticator" ) //go:generate mockgen -source client.go -destination=mocks/client.go -package=mocks Client type Client interface { // GetActiveBundle retrieves the currently active bundle. GetActiveBundle(ctx context.Context, clusterName string) (activeBundle *api.PackageBundle, err error) // GetPackageBundleController retrieves clusters package bundle controller GetPackageBundleController(ctx context.Context, clusterName string) (controller *api.PackageBundleController, err error) // GetBundleList get list of bundles worthy of consideration GetBundleList(ctx context.Context) (bundles []api.PackageBundle, err error) // GetBundle retrieves the named bundle. GetBundle(ctx context.Context, name string) (namedBundle *api.PackageBundle, err error) // GetSecret retries the named secret GetSecret(ctx context.Context, name string) (secret *v1.Secret, err error) // CreateBundle add a new bundle custom resource CreateBundle(ctx context.Context, bundle *api.PackageBundle) error // CreateClusterConfigMap based on cluster name CreateClusterConfigMap(ctx context.Context, clusterName string) error // CreatePackage creates a package CreatePackage(ctx context.Context, pkg *api.Package) (err error) // GetPackageList retrieves the list of packages resources. GetPackageList(ctx context.Context, namespace string) (packages api.PackageList, err error) // SaveStatus saves a resource status SaveStatus(ctx context.Context, object client.Object) error // Save saves a resource Save(ctx context.Context, object client.Object) error } type managerClient struct { client.Client } func NewManagerClient(client client.Client) *managerClient { return &(managerClient{ Client: client, }) } var _ Client = (*managerClient)(nil) // GetActiveBundle retrieves the bundle from which package are installed. // // It retrieves the name of the active bundle from the PackageBundleController, // then uses the K8s API to retrieve and return the active bundle. func (bc *managerClient) GetActiveBundle(ctx context.Context, clusterName string) (activeBundle *api.PackageBundle, err error) { pbc, err := bc.GetPackageBundleController(ctx, clusterName) if err != nil { return nil, err } if pbc.Spec.ActiveBundle == "" { return nil, fmt.Errorf("no activeBundle set in PackageBundleController") } nn := types.NamespacedName{ Namespace: api.PackageNamespace, Name: pbc.Spec.ActiveBundle, } activeBundle = &api.PackageBundle{} err = bc.Get(ctx, nn, activeBundle) if err != nil { return nil, err } return activeBundle, nil } func (bc *managerClient) GetPackageBundleController(ctx context.Context, clusterName string) (*api.PackageBundleController, error) { if clusterName == "" { clusterName = os.Getenv("CLUSTER_NAME") } pbc := api.PackageBundleController{} key := types.NamespacedName{ Namespace: api.PackageNamespace, Name: clusterName, } err := bc.Get(ctx, key, &pbc) if err != nil { return nil, fmt.Errorf("getting PackageBundleController: %v", err) } return &pbc, nil } func (bc *managerClient) GetBundle(ctx context.Context, name string) (namedBundle *api.PackageBundle, err error) { nn := types.NamespacedName{ Namespace: api.PackageNamespace, Name: name, } namedBundle = &api.PackageBundle{} err = bc.Get(ctx, nn, namedBundle) if err != nil { if apierrors.IsNotFound(err) { return nil, nil } return nil, err } return namedBundle, nil } func (bc *managerClient) GetSecret(ctx context.Context, name string) (secret *v1.Secret, err error) { nn := types.NamespacedName{ Namespace: api.PackageNamespace, Name: name, } secret = &v1.Secret{} err = bc.Get(ctx, nn, secret) if err != nil { if apierrors.IsNotFound(err) { return nil, nil } return nil, fmt.Errorf("aws-secret error:%v", err) } return secret, nil } func (bc *managerClient) GetBundleList(ctx context.Context) (bundles []api.PackageBundle, err error) { allBundles := &api.PackageBundleList{} err = bc.Client.List(ctx, allBundles, &client.ListOptions{Namespace: api.PackageNamespace}) if err != nil { return nil, fmt.Errorf("listing package bundles: %s", err) } return allBundles.Items, nil } func (bc *managerClient) CreateClusterNamespace(ctx context.Context, clusterName string) error { name := api.PackageNamespace + "-" + clusterName key := types.NamespacedName{ Name: name, } ns := &v1.Namespace{} err := bc.Get(ctx, key, ns) // Nil err check here means that the namespace exists thus we can just return with no error if err == nil { return nil } if !apierrors.IsNotFound(err) { return err } ns.Name = name err = bc.Client.Create(ctx, ns) if err != nil { return err } return nil } func (bc *managerClient) CreateClusterConfigMap(ctx context.Context, clusterName string) error { name := auth.ConfigMapName namespace := api.PackageNamespace + "-" + clusterName key := types.NamespacedName{ Name: name, Namespace: namespace, } cm := &v1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, }, } err := bc.Get(ctx, key, cm) // Nil err check here means that the config map exists thus we can just return with no error if err == nil { return nil } if !apierrors.IsNotFound(err) { return err } cm.Data = make(map[string]string) cm.Data[namespace] = "eksa-package-controller" // Unfortunate workaround for emissary webhooks hard coded crd namespace cm.Data["emissary-system"] = "eksa-package-placeholder" err = bc.Client.Create(ctx, cm) if err != nil { return err } return nil } // CreatePackage creates the given package resource func (p *managerClient) CreatePackage(ctx context.Context, pkg *api.Package) (err error) { return p.Client.Create(ctx, pkg) } // GetPackageList retrieves all packages present in the given namespace func (p *managerClient) GetPackageList(ctx context.Context, namespace string) (packages api.PackageList, err error) { list := api.PackageList{} return list, p.Client.List(ctx, &list, client.InNamespace(namespace)) } func (bc *managerClient) CreateBundle(ctx context.Context, bundle *api.PackageBundle) error { err := bc.Client.Create(ctx, bundle) if err != nil { return fmt.Errorf("creating new package bundle: %s", err) } return nil } func (bc *managerClient) SaveStatus(ctx context.Context, object client.Object) error { return bc.Client.Status().Update(ctx, object, &client.SubResourceUpdateOptions{}) } func (bc *managerClient) Save(ctx context.Context, object client.Object) error { return bc.Client.Update(ctx, object, &client.UpdateOptions{}) }