diff --git a/README.md b/README.md index fab140cd2ea..7d60a636459 100644 --- a/README.md +++ b/README.md @@ -5,55 +5,38 @@ MinIO Operator brings native support for [MinIO](https://github.com/minio/minio) ## Prerequisites - Kubernetes >= v1.17.0. -- Create PVs. We recommend [direct CSI driver](https://github.com/minio/operator/blob/master/docs/using-direct-csi.md) for PV creation. -- Install [`kubectl minio` plugin](https://github.com/minio/operator/tree/master/kubectl-minio#install-plugin). +- Create PVs. +- Install [`kubectl minio` plugin using `krew install minio`. ## Operator Setup MinIO Operator offers MinIO Tenant creation, management, upgrade, zone addition and more. Operator is meant to control and manage multiple MinIO Tenants. -To get started, create the MinIO Operator deployment. This is a _one time_ process. +To get started, initialize the MinIO Operator deployment. This is a _one time_ process. ```sh -kubectl minio operator create +kubectl minio init ``` Once the MinIO Operator is created, proceed with Tenant creation. ## Tenant Setup -A Tenant is a MinIO cluster created and managed by the Operator. +A Tenant is a MinIO cluster created and managed by the Operator. Before creating tenant, please ensure you have requisite nodes and drives in place and relevant PVs are created. -### Step 1: Create Tenant Namespace +In below example, we ask MinIO Operator to create a Tenant yaml with 4 nodes, 16 volumes, and 16 Ti total raw capacity (4 volumes of 1 Ti per node). This means you need to have 4 PVs of 1Ti each per node, with a total of 4 nodes, before attempting to create the MinIO tenant. -Before creating a Tenant, please create the namespace where this Tenant will reside. - -For logical isolation, Operator allows a single Tenant per Kubernetes Namespace. - -```sh -kubectl create ns tenant1-ns -``` - -### Step 2: Create Secret for Tenant Credentials - -Next, create the Kubernetes secret that encapsulates root credentials for MinIO Tenant. Please ensure to create secret object with literals `accesskey` and `secretkey`. - -Remember to change `YOUR-ACCESS-KEY` and `YOUR-SECRET-KEY` to actual values. +We recommend [direct CSI driver](https://github.com/minio/operator/blob/master/docs/using-direct-csi.md) to create PVs. ```sh -kubectl create secret generic tenant1-secret --from-literal=accesskey=YOUR-ACCESS-KEY --from-literal=secretkey=YOUR-SECRET-KEY --namespace tenant1-ns +kubectl minio tenant create --name tenant1 --secret tenant1-secret --servers 4 --volumes 16 --capacity 16Ti ``` -Note that the access key and secret key provided here is authorized to perform _all_ operations on the Tenant. - -### Step 3: Create MinIO Tenant - -We can create the Tenant now. Before that, please ensure you have requisite nodes and drives in place and relevant PVs are created. In below example, we ask MinIO Operator to create a Tenant with 4 nodes, 16 volumes, and 16 Ti total raw capacity (4 volumes of 1 Ti per node). This means you need to have 4 PVCs of 1 Ti each, per node, and total of 4 nodes, before attempting to create the MinIO Tenant. - -We recommend [direct CSI driver](https://github.com/minio/operator/blob/master/docs/using-direct-csi.md) to create PVs. +Optionally, you can generate a yaml file with the `-o` flag in above command and modify the yaml file as per your specific requirements. Once you verify and optionally add any other relevant fields to the file, create the tenant ```sh -kubectl minio tenant create --name tenant1 --secret tenant1-secret --servers 4 --volumes 16 --capacity 16Ti --namespace tenant1-ns --storage-class direct.csi.min.io +kubectl minio tenant create --name tenant1 --secret tenant1-secret --servers 4 --volumes 16 --capacity 16Ti -o > tenant.yaml +kubectl apply -f tenant.yaml ``` ## Post Tenant Creation @@ -63,7 +46,7 @@ kubectl minio tenant create --name tenant1 --secret tenant1-secret --servers 4 - You can add capacity to the tenant using `kubectl minio` plugin, like this ``` -kubectl minio tenant volume add --name tenant1 --servers 8 --volumes 32 --capacity 32Ti --namespace tenant1-ns +kubectl minio tenant expand --name tenant1 --servers 8 --volumes 32 --capacity 32Ti ``` This will add 32 drives spread uniformly over 8 servers to the tenant `tenant1`, with additional capacity of 32Ti. Read more about [tenant expansion here](https://github.com/minio/operator/blob/master/docs/expansion.md). diff --git a/examples/tenant-encryption.yaml b/examples/tenant-encryption.yaml index 68f51099295..52c1708ba09 100644 --- a/examples/tenant-encryption.yaml +++ b/examples/tenant-encryption.yaml @@ -72,7 +72,7 @@ stringData: # retry: 15s # Duration until the server tries to re-authenticate after connection loss. # tls: # The Vault client TLS configuration for mTLS authentication and certificate verification # key: "" # Path to the TLS client private key for mTLS authentication to Vault - # cert: "" # Path to the TLS client certificate for mTLS authentication to Vailt + # cert: "" # Path to the TLS client certificate for mTLS authentication to Vault # ca: "" # Path to one or multiple PEM root CA certificates # status: # Vault status configuration. The server will periodically reach out to Vault to check its status. # ping: 10s # Duration until the server checks Vault's status again. diff --git a/kubectl-minio/README.md b/kubectl-minio/README.md index 19c9bc7b81f..75ebefaf8da 100644 --- a/kubectl-minio/README.md +++ b/kubectl-minio/README.md @@ -3,18 +3,18 @@ ## Prerequisites - Kubernetes >= v1.17.0. -- Create PVs using [direct CSI driver](https://github.com/minio/operator/blob/master/docs/using-direct-csi.md). - kubectl installed on your local machine, configured to talk to the Kubernetes cluster. +- Create PVs. ## Install Plugin -Download the Kubectl plugin binary from [plugin release page](https://github.com/minio/operator/releases), and place the binary in executable path on your machine (e.g. `/usr/local/bin`). +Command: `kubectl krew install minio` ## Plugin Commands ### Operator Deployment -Command: `kubectl minio operator create` +Command: `kubectl minio init [options]` Creates MinIO Operator Deployment along with MinIO Tenant CRD, Service account, Cluster Role and Cluster Role Binding. @@ -22,55 +22,57 @@ Options: - `--image=minio/k8s-operator:3.0.5` - `--namespace=minio-operator` -- `--service-account=minio-operator` - `--cluster-domain=cluster.local` - `--namespace-to-watch=default` - `--image-pull-secret=` - `--output` +### Operator Deletion + +Command: `kubectl minio delete [options]` + +Deletes MinIO Operator Deployment along with MinIO Tenant CRD, Service account, Cluster Role and Cluster Role Binding. It also removes all the Tenant instances. + +Options: + +- `--namespace=minio-operator` + ### Tenant #### MinIO Tenant Creation -Command: `kubectl minio tenant create --name TENANT_NAME --secret SECRET_NAME --servers SERVERS --volumes TOTAL_VOLUMES --capacity TOTAL_RAW_CAPACITY [options]` +Command: `kubectl minio tenant create --name TENANT_NAME --servers SERVERS --volumes TOTAL_VOLUMES --capacity TOTAL_RAW_CAPACITY [options]` Creates a MinIO Tenant based on the passed values. -example: `kubectl minio tenant create --name tenant1 --secret cred-secret --servers 4 --volumes 16 --capacity 16Ti` +example: `kubectl minio tenant create --name tenant1 --servers 4 --volumes 16 --capacity 16Ti` Options: - `--namespace=minio` -- `--image=minio/minio:RELEASE.2020-09-26T03-44-56Z` -- `--storageClass=local` -- `--kms-secret=secret-name` -- `--console-secret=secret-name` -- `--cert-secret=secret-name` -- `--image-pull-secret=` +- `--kes-config=kes-secret` - `--output` #### Add Tenant Zones -Command: `kubectl minio tenant volume add --name TENANT_NAME --servers SERVERS --volumes TOTAL_VOLUMES --capacity TOTAL_RAW_CAPACITY [options]` +Command: `kubectl minio tenant expand --name TENANT_NAME --servers SERVERS --volumes TOTAL_VOLUMES --capacity TOTAL_RAW_CAPACITY [options]` Add new volumes (and nodes) to existing MinIO Tenant. -example: `kubectl minio tenant volume add --name cluster1 --servers 4 --volumes 16 --capacity 16Ti` +example: `kubectl minio tenant expand --name tenant1 --servers 4 --volumes 16 --capacity 16Ti` Options: - `--namespace=minio` -- `--storageClass=local` -- `--image-pull-secret=` - `--output` #### List Tenant Zones -Command: `kubectl minio tenant volume list --name TENANT_NAME [options]` +Command: `kubectl minio tenant info --name TENANT_NAME [options]` List all existing MinIO Zones in the given MinIO Tenant. -example: `kubectl minio tenant volume list --name cluster1` +example: `kubectl minio tenant info --name tenant1` Options: @@ -82,12 +84,11 @@ Command: `kubectl minio tenant upgrade --name TENANT_NAME --image IMAGE_TAG [opt Upgrade MinIO Docker image for the given MinIO Tenant. -example: `kubectl minio tenant upgrade --name cluster1 --image minio/minio:edge` +example: `kubectl minio tenant upgrade --name tenant1 --image minio/minio:RELEASE.2020-09-26T03-44-56Z` Options: - `--namespace=minio` -- `--image-pull-secret=` - `--output` #### Remove Tenant diff --git a/kubectl-minio/cmd/delete.go b/kubectl-minio/cmd/delete.go new file mode 100644 index 00000000000..6cf67298e41 --- /dev/null +++ b/kubectl-minio/cmd/delete.go @@ -0,0 +1,124 @@ +/* + * This file is part of MinIO Operator + * Copyright (C) 2020, MinIO, Inc. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +package cmd + +import ( + "bufio" + "context" + "errors" + "fmt" + "io" + "os" + "strings" + + "github.com/minio/kubectl-minio/cmd/helpers" + "github.com/minio/kubectl-minio/cmd/resources" + "github.com/spf13/cobra" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +const ( + deleteDesc = ` +'delete' command delete MinIO Operator deployment along with all the tenants.` + deleteExample = ` kubectl minio delete` +) + +type deleteCmd struct { + out io.Writer + errOut io.Writer + output bool + operatorOpts resources.OperatorOptions + steps []runtime.Object +} + +func newDeleteCmd(out io.Writer, errOut io.Writer) *cobra.Command { + o := &deleteCmd{out: out, errOut: errOut} + + cmd := &cobra.Command{ + Use: "delete", + Short: "Delete MinIO Operator deployment", + Long: deleteDesc, + Example: deleteExample, + PreRunE: func(cmd *cobra.Command, args []string) error { + fmt.Printf("Are you sure you want to delete ALL the MinIO Tenants and MinIO Operator? [Y/N]: ") + if !o.ask() { + return errors.New("Aborting Operator deletion") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) != 0 { + return errors.New("this command does not accept arguments") + } + return o.run() + }, + } + + f := cmd.Flags() + f.StringVarP(&o.operatorOpts.NS, "namespace", "n", helpers.DefaultNamespace, "namespace scope for this request") + + return cmd +} + +func (o *deleteCmd) ask() bool { + reader := bufio.NewReader(os.Stdin) + for { + s, _ := reader.ReadString('\n') + s = strings.TrimSuffix(s, "\n") + s = strings.ToLower(s) + if len(s) > 1 { + fmt.Fprintln(os.Stderr, "Please enter Y or N") + continue + } + if strings.Compare(s, "n") == 0 { + return false + } else if strings.Compare(s, "y") == 0 { + break + } else { + continue + } + } + return true +} + +func (o *deleteCmd) run() error { + client, err := helpers.GetKubeClient() + if err != nil { + return err + } + extclient, err := helpers.GetKubeExtensionClient() + if err != nil { + return err + } + if err := extclient.ApiextensionsV1().CustomResourceDefinitions().Delete(context.Background(), crdObj.Name, v1.DeleteOptions{}); err != nil { + return err + } + if err := client.RbacV1().ClusterRoles().Delete(context.Background(), crObj.Name, v1.DeleteOptions{}); err != nil { + return err + } + if err := client.CoreV1().ServiceAccounts(o.operatorOpts.NS).Delete(context.Background(), helpers.DefaultServiceAccount, v1.DeleteOptions{}); err != nil { + return err + } + if err := client.RbacV1().ClusterRoleBindings().Delete(context.Background(), helpers.ClusterRoleBindingName, v1.DeleteOptions{}); err != nil { + return err + } + return client.AppsV1().Deployments(o.operatorOpts.NS).Delete(context.Background(), helpers.DeploymentName, v1.DeleteOptions{}) +} diff --git a/kubectl-minio/cmd/helpers/helpers.go b/kubectl-minio/cmd/helpers/helpers.go index b6482216ac0..f13e7c8464a 100644 --- a/kubectl-minio/cmd/helpers/helpers.go +++ b/kubectl-minio/cmd/helpers/helpers.go @@ -25,10 +25,10 @@ import ( "os/exec" operatorv1 "github.com/minio/operator/pkg/client/clientset/versioned" - "gopkg.in/yaml.v2" apiextension "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/yaml" "github.com/pkg/errors" "k8s.io/client-go/kubernetes" diff --git a/kubectl-minio/cmd/operator-create.go b/kubectl-minio/cmd/init.go similarity index 87% rename from kubectl-minio/cmd/operator-create.go rename to kubectl-minio/cmd/init.go index 43ac816a245..ecc1cd72636 100644 --- a/kubectl-minio/cmd/operator-create.go +++ b/kubectl-minio/cmd/init.go @@ -41,12 +41,12 @@ import ( ) const ( - operatorCreateDesc = ` -'create' command creates MinIO Operator deployment along with all the dependencies.` - operatorCreateExample = ` kubectl minio operator create` + operatorInitDesc = ` +'init' command creates MinIO Operator deployment along with all the dependencies.` + operatorInitExample = ` kubectl minio operator init` ) -type operatorCreateCmd struct { +type operatorInitCmd struct { out io.Writer errOut io.Writer output bool @@ -54,14 +54,14 @@ type operatorCreateCmd struct { steps []runtime.Object } -func newOperatorCreateCmd(out io.Writer, errOut io.Writer) *cobra.Command { - o := &operatorCreateCmd{out: out, errOut: errOut} +func newInitCmd(out io.Writer, errOut io.Writer) *cobra.Command { + o := &operatorInitCmd{out: out, errOut: errOut} cmd := &cobra.Command{ - Use: "create", - Short: "Create MinIO Operator deployment", - Long: operatorCreateDesc, - Example: operatorCreateExample, + Use: "init", + Short: "Initialize MinIO Operator deployment", + Long: operatorInitDesc, + Example: operatorInitExample, RunE: func(cmd *cobra.Command, args []string) error { if len(args) != 0 { return errors.New("this command does not accept arguments") @@ -74,7 +74,6 @@ func newOperatorCreateCmd(out io.Writer, errOut io.Writer) *cobra.Command { f.StringVarP(&o.operatorOpts.Image, "image", "i", helpers.DefaultOperatorImage, "operator image") f.StringVarP(&o.operatorOpts.NS, "namespace", "n", helpers.DefaultNamespace, "namespace scope for this request") f.StringVarP(&o.operatorOpts.ClusterDomain, "cluster-domain", "d", helpers.DefaultClusterDomain, "cluster domain of the Kubernetes cluster") - f.StringVar(&o.operatorOpts.ServiceAccount, "service-account", helpers.DefaultServiceAccount, "service account for operator") f.StringVar(&o.operatorOpts.NSToWatch, "namespace-to-watch", "", "namespace where operator looks for MinIO tenants, leave empty for all namespaces") f.StringVar(&o.operatorOpts.ImagePullSecret, "image-pull-secret", "", "image pull secret to be used for pulling operator image") f.BoolVarP(&o.output, "output", "o", false, "dry run this command and generate requisite yaml") @@ -83,9 +82,9 @@ func newOperatorCreateCmd(out io.Writer, errOut io.Writer) *cobra.Command { } // run initializes local config and installs MinIO Operator to Kubernetes cluster. -func (o *operatorCreateCmd) run() error { - sa := resources.NewServiceAccountForOperator(o.operatorOpts.ServiceAccount, o.operatorOpts.NS) - crb := resources.NewCluterRoleBindingForOperator(o.operatorOpts.NS, o.operatorOpts.ServiceAccount) +func (o *operatorInitCmd) run() error { + sa := resources.NewServiceAccountForOperator(helpers.DefaultServiceAccount, o.operatorOpts.NS) + crb := resources.NewCluterRoleBindingForOperator(helpers.DefaultServiceAccount, o.operatorOpts.NS) d := resources.NewDeploymentForOperator(o.operatorOpts) if !o.output { diff --git a/kubectl-minio/cmd/kubectl-minio.go b/kubectl-minio/cmd/kubectl-minio.go index 53a08425354..c9c00b6f452 100644 --- a/kubectl-minio/cmd/kubectl-minio.go +++ b/kubectl-minio/cmd/kubectl-minio.go @@ -101,7 +101,8 @@ func NewCmdMinIO(streams genericclioptions.IOStreams) *cobra.Command { SilenceUsage: true, } - cmd.AddCommand(newOperatorCmd(cmd.OutOrStdout(), cmd.ErrOrStderr())) + cmd.AddCommand(newInitCmd(cmd.OutOrStdout(), cmd.ErrOrStderr())) + cmd.AddCommand(newDeleteCmd(cmd.OutOrStdout(), cmd.ErrOrStderr())) cmd.AddCommand(newTenantCmd(cmd.OutOrStdout(), cmd.ErrOrStderr())) return cmd diff --git a/kubectl-minio/cmd/operator.go b/kubectl-minio/cmd/operator.go deleted file mode 100644 index 883b369d2dd..00000000000 --- a/kubectl-minio/cmd/operator.go +++ /dev/null @@ -1,41 +0,0 @@ -/* - * This file is part of MinIO Operator - * Copyright (C) 2020, MinIO, Inc. - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see - * - */ - -package cmd - -import ( - "io" - - "github.com/spf13/cobra" -) - -const ( - operatorDesc = ` -'operator' is the top level command for managing MinIO operator.` -) - -func newOperatorCmd(out io.Writer, errOut io.Writer) *cobra.Command { - cmd := &cobra.Command{ - Use: "operator", - Short: "Create MinIO operator", - Long: operatorDesc, - } - - cmd.AddCommand(newOperatorCreateCmd(cmd.OutOrStdout(), cmd.ErrOrStderr())) - return cmd -} diff --git a/kubectl-minio/cmd/resources/cluter-role-binding.go b/kubectl-minio/cmd/resources/cluter-role-binding.go index f1e2508e971..82da089d1da 100644 --- a/kubectl-minio/cmd/resources/cluter-role-binding.go +++ b/kubectl-minio/cmd/resources/cluter-role-binding.go @@ -25,7 +25,7 @@ import ( ) // NewCluterRoleBindingForOperator will return a new cluster-role-binding for a MinIO Operator -func NewCluterRoleBindingForOperator(ns, saName string) *rbacv1.ClusterRoleBinding { +func NewCluterRoleBindingForOperator(saName, ns string) *rbacv1.ClusterRoleBinding { return &rbacv1.ClusterRoleBinding{ ObjectMeta: metav1.ObjectMeta{ Name: helpers.ClusterRoleBindingName, diff --git a/kubectl-minio/cmd/resources/common.go b/kubectl-minio/cmd/resources/common.go index 88c375c3d4d..cfeb4fa7602 100644 --- a/kubectl-minio/cmd/resources/common.go +++ b/kubectl-minio/cmd/resources/common.go @@ -23,6 +23,7 @@ import ( miniov1 "github.com/minio/operator/pkg/apis/minio.min.io/v1" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func tenantStorage(q resource.Quantity) v1.ResourceList { @@ -37,6 +38,10 @@ func Zone(servers, volumes int32, q resource.Quantity, sc string) miniov1.Zone { Servers: servers, VolumesPerServer: volumes, VolumeClaimTemplate: &v1.PersistentVolumeClaim{ + TypeMeta: metav1.TypeMeta{ + Kind: v1.ResourcePersistentVolumeClaims.String(), + APIVersion: v1.SchemeGroupVersion.Version, + }, Spec: v1.PersistentVolumeClaimSpec{ AccessModes: []v1.PersistentVolumeAccessMode{helpers.MinIOAccessMode}, Resources: v1.ResourceRequirements{ diff --git a/kubectl-minio/cmd/resources/deployment.go b/kubectl-minio/cmd/resources/deployment.go index d35a43b7435..245b5d6a9e3 100644 --- a/kubectl-minio/cmd/resources/deployment.go +++ b/kubectl-minio/cmd/resources/deployment.go @@ -32,7 +32,6 @@ type OperatorOptions struct { NS string NSToWatch string ClusterDomain string - ServiceAccount string ImagePullSecret string } @@ -83,7 +82,7 @@ func NewDeploymentForOperator(opts OperatorOptions) *appsv1.Deployment { Labels: operatorLabels(), }, Spec: corev1.PodSpec{ - ServiceAccountName: opts.ServiceAccount, + ServiceAccountName: helpers.DefaultServiceAccount, Containers: []corev1.Container{container(opts.Image, opts.ClusterDomain, opts.NSToWatch)}, ImagePullSecrets: []corev1.LocalObjectReference{{Name: opts.ImagePullSecret}}, }, diff --git a/kubectl-minio/cmd/resources/secret.go b/kubectl-minio/cmd/resources/secret.go index 876d244ef91..11b9e6eeacf 100644 --- a/kubectl-minio/cmd/resources/secret.go +++ b/kubectl-minio/cmd/resources/secret.go @@ -19,24 +19,55 @@ package resources import ( + "github.com/google/uuid" corev1 "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func secretData(accessKey, secretKey string) map[string][]byte { +func tenantSecretData() map[string][]byte { m := make(map[string][]byte, 2) - m["accesskey"] = []byte(accessKey) - m["secretkey"] = []byte(secretKey) + m["accesskey"] = []byte(uuid.New().String()) + m["secretkey"] = []byte(uuid.New().String()) return m } -// NewSecretForTenant will return a new secret a MinIO Tenant tenant -func NewSecretForTenant(name, ns, accessKey, secretKey string) *corev1.Secret { +func consoleSecretData() map[string][]byte { + m := make(map[string][]byte, 5) + m["CONSOLE_ACCESS_KEY"] = []byte(uuid.New().String()) + m["CONSOLE_SECRET_KEY"] = []byte(uuid.New().String()) + m["CONSOLE_HMAC_JWT_SECRET"] = []byte(uuid.New().String()) + m["CONSOLE_PBKDF_PASSPHRASE"] = []byte(uuid.New().String()) + m["CONSOLE_PBKDF_SALT"] = []byte(uuid.New().String()) + return m +} + +// NewSecretForTenant will return a new secret a MinIO Tenant +func NewSecretForTenant(opts *TenantOptions) *corev1.Secret { return &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: ns, + Name: opts.SecretName, + Namespace: opts.NS, + }, + TypeMeta: metav1.TypeMeta{ + Kind: "Secret", + APIVersion: v1.SchemeGroupVersion.Version, + }, + Data: tenantSecretData(), + } +} + +// NewSecretForConsole will return a new secret a MinIO Tenant Console +func NewSecretForConsole(opts *TenantOptions) *corev1.Secret { + return &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: opts.ConsoleSecret, + Namespace: opts.NS, + }, + TypeMeta: metav1.TypeMeta{ + Kind: "Secret", + APIVersion: v1.SchemeGroupVersion.Version, }, - Data: secretData(accessKey, secretKey), + Data: consoleSecretData(), } } diff --git a/kubectl-minio/cmd/resources/tenant.go b/kubectl-minio/cmd/resources/tenant.go index e33c0b7816d..9ce5dd52259 100644 --- a/kubectl-minio/cmd/resources/tenant.go +++ b/kubectl-minio/cmd/resources/tenant.go @@ -22,6 +22,7 @@ import ( "errors" helpers "github.com/minio/kubectl-minio/cmd/helpers" + operator "github.com/minio/operator/pkg/apis/minio.min.io" miniov1 "github.com/minio/operator/pkg/apis/minio.min.io/v1" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" @@ -123,14 +124,14 @@ func tenantKESConfig(tenant, secret string) *miniov1.KESConfig { func tenantConsoleConfig(tenant, secret string) *miniov1.ConsoleConfiguration { if secret != "" { return &miniov1.ConsoleConfiguration{ + Metadata: &metav1.ObjectMeta{ + Name: tenant, + }, Replicas: helpers.ConsoleReplicas, Image: helpers.DefaultConsoleImage, ConsoleSecret: &v1.LocalObjectReference{ Name: secret, }, - Metadata: &metav1.ObjectMeta{ - Labels: tenantConsoleLabels(tenant), - }, } } return nil @@ -152,7 +153,7 @@ func storageClass(sc string) *string { return nil } -// NewTenant will return a new minioinstance for a MinIO Operator +// NewTenant will return a new Tenant for a MinIO Operator func NewTenant(opts *TenantOptions) (*miniov1.Tenant, error) { volumesPerServer := helpers.VolumesPerServer(opts.Volumes, opts.Servers) capacityPerVolume, err := helpers.CapacityPerVolume(opts.Capacity, opts.Volumes) @@ -161,12 +162,15 @@ func NewTenant(opts *TenantOptions) (*miniov1.Tenant, error) { } t := &miniov1.Tenant{ + TypeMeta: metav1.TypeMeta{ + Kind: "Tenant", + APIVersion: operator.GroupName + "/" + miniov1.Version, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: opts.Name, + Namespace: opts.NS, + }, Spec: miniov1.TenantSpec{ - Metadata: &metav1.ObjectMeta{ - Name: opts.Name, - Labels: tenantLabels(opts.Name), - Annotations: tenantAnnotations(), - }, Image: opts.Image, ServiceName: helpers.ServiceName(opts.Name), CredsSecret: &v1.LocalObjectReference{ diff --git a/kubectl-minio/cmd/tenant-create.go b/kubectl-minio/cmd/tenant-create.go index 631ebcc61a0..2ba9a053c7e 100644 --- a/kubectl-minio/cmd/tenant-create.go +++ b/kubectl-minio/cmd/tenant-create.go @@ -20,14 +20,16 @@ package cmd import ( "context" - "errors" "fmt" "io" "github.com/minio/kubectl-minio/cmd/helpers" "github.com/minio/kubectl-minio/cmd/resources" - "gopkg.in/yaml.v2" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "sigs.k8s.io/yaml" miniov1 "github.com/minio/operator/pkg/apis/minio.min.io/v1" operatorv1 "github.com/minio/operator/pkg/client/clientset/versioned" @@ -37,7 +39,9 @@ import ( const ( createDesc = ` 'create' command creates a new MinIO tenant` - createExample = ` kubectl minio tenant create --name tenant1 --secret tenant1-creds --servers 4 --volumes 16 --capacity 16Ti --namespace tenant1-ns` + createExample = ` kubectl minio tenant create --name tenant1 --secret tenant1-creds --servers 4 --volumes 16 --capacity 16Ti --namespace tenant1-ns` + tenantSecretSuffix = "-creds-secret" + consoleSecretSuffix = "-console-secret" ) type createCmd struct { @@ -47,7 +51,7 @@ type createCmd struct { tenantOpts resources.TenantOptions } -func newCreateCmd(out io.Writer, errOut io.Writer) *cobra.Command { +func newTenantCreateCmd(out io.Writer, errOut io.Writer) *cobra.Command { c := &createCmd{out: out, errOut: errOut} cmd := &cobra.Command{ @@ -65,62 +69,83 @@ func newCreateCmd(out io.Writer, errOut io.Writer) *cobra.Command { f := cmd.Flags() f.StringVar(&c.tenantOpts.Name, "name", "", "name of the MinIO tenant to create") - f.StringVar(&c.tenantOpts.SecretName, "secret", "", "secret name used for tenant credentials") f.Int32Var(&c.tenantOpts.Servers, "servers", 0, "total number of pods in MinIO tenant") f.Int32Var(&c.tenantOpts.Volumes, "volumes", 0, "total number of volumes in the MinIO tenant") f.StringVar(&c.tenantOpts.Capacity, "capacity", "", "total raw capacity of MinIO tenant in this zone, e.g. 16Ti") f.StringVarP(&c.tenantOpts.NS, "namespace", "n", helpers.DefaultNamespace, "namespace scope for this request") - f.StringVarP(&c.tenantOpts.Image, "image", "i", helpers.DefaultTenantImage, "image to be used for MinIO") - f.StringVarP(&c.tenantOpts.StorageClass, "storage-class", "s", "", "storage class to be used while PVC creation") - f.StringVar(&c.tenantOpts.KmsSecret, "kms-secret", "", "secret with details for enabled encryption") - f.StringVar(&c.tenantOpts.ConsoleSecret, "console-secret", "", "secret with details for MinIO console deployment") - f.StringVar(&c.tenantOpts.CertSecret, "cert-secret", "", "secret with external certificates for MinIO deployment") - f.StringVar(&c.tenantOpts.ImagePullSecret, "image-pull-secrets", "", "image pull secret to be used for pulling MinIO image") - f.BoolVar(&c.tenantOpts.DisableTLS, "disable-tls", false, "disable automatic certificate creation for MinIO peer connection") + f.StringVar(&c.tenantOpts.KmsSecret, "kes-config", "", "name of secret with details for enabling encryption, refer example https://github.com/minio/operator/blob/master/examples/kes-secret.yaml") f.BoolVarP(&c.output, "output", "o", false, "dry run this command and generate requisite yaml") return cmd } func (c *createCmd) validate() error { - if c.tenantOpts.SecretName == "" { - return errors.New("--secret flag is required for tenant creation") - } + c.tenantOpts.SecretName = c.tenantOpts.Name + tenantSecretSuffix + c.tenantOpts.ConsoleSecret = c.tenantOpts.Name + consoleSecretSuffix + c.tenantOpts.Image = helpers.DefaultTenantImage return c.tenantOpts.Validate() } // run initializes local config and installs MinIO Operator to Kubernetes cluster. func (c *createCmd) run(args []string) error { - // Create operator client + // Create operator and kube client oclient, err := helpers.GetKubeOperatorClient() if err != nil { return err } + kclient, err := helpers.GetKubeClient() + if err != nil { + return err + } + // generate the resources t, err := resources.NewTenant(&c.tenantOpts) if err != nil { return err } - t.ObjectMeta.Name = c.tenantOpts.Name - t.ObjectMeta.Namespace = c.tenantOpts.NS + s := resources.NewSecretForTenant(&c.tenantOpts) + console := resources.NewSecretForConsole(&c.tenantOpts) + // create resources if !c.output { - return createTenant(oclient, t) + return createTenant(oclient, kclient, t, s, console) } - - o, err := yaml.Marshal(&t) + ot, err := yaml.Marshal(&t) + if err != nil { + return err + } + os, err := yaml.Marshal(&s) if err != nil { return err } - fmt.Println(string(o)) + oc, err := yaml.Marshal(&console) + if err != nil { + return err + } + fmt.Println(string(ot)) + fmt.Println("---") + fmt.Println(string(os)) + fmt.Println("---") + fmt.Println(string(oc)) return nil } -func createTenant(client *operatorv1.Clientset, t *miniov1.Tenant) error { - _, err := client.MinioV1().Tenants(t.Namespace).Create(context.Background(), t, v1.CreateOptions{}) +func createTenant(oclient *operatorv1.Clientset, kclient *kubernetes.Clientset, t *miniov1.Tenant, s, console *corev1.Secret) error { + if _, err := kclient.CoreV1().Secrets(t.Namespace).Create(context.Background(), s, metav1.CreateOptions{}); err != nil { + return err + } + if _, err := kclient.CoreV1().Secrets(t.Namespace).Create(context.Background(), console, metav1.CreateOptions{}); err != nil { + return err + } + to, err := oclient.MinioV1().Tenants(t.Namespace).Create(context.Background(), t, v1.CreateOptions{}) if err != nil { return err } - fmt.Printf("MinIO Tenant %s: created\n", t.ObjectMeta.Name) + fmt.Printf("MinIO Tenant %s Created\n\n", to.ObjectMeta.Name) + fmt.Printf("Tenant\nAccess Key: %s\nSecret Key: %s\nVersion: %s\nClusterIP Service: %s\n\n", s.Data["accesskey"], s.Data["secretkey"], to.Spec.Image, to.Spec.ServiceName) + fmt.Printf("MinIO Console\nAccess Key: %s\nSecret Key: %s\nVersion: %s\nClusterIP Service: %s\n\n", console.Data["CONSOLE_ACCESS_KEY"], console.Data["CONSOLE_SECRET_KEY"], to.Spec.Console.Image, to.Spec.Console.Metadata.Name+miniov1.ConsoleName) + if t.Spec.KES != nil { + fmt.Printf("KES Version: %s\n", t.Spec.KES.Image) + } return nil } diff --git a/kubectl-minio/cmd/tenant-delete.go b/kubectl-minio/cmd/tenant-delete.go index 43df6f968ba..4f7ac053116 100644 --- a/kubectl-minio/cmd/tenant-delete.go +++ b/kubectl-minio/cmd/tenant-delete.go @@ -27,26 +27,27 @@ import ( "github.com/minio/kubectl-minio/cmd/helpers" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" operatorv1 "github.com/minio/operator/pkg/client/clientset/versioned" "github.com/spf13/cobra" ) const ( - deleteDesc = ` + tenantDeleteDesc = ` 'delete' command deletes a MinIO tenant` - deleteExample = ` kubectl minio tenant delete --name tenant1 --namespace tenant1-ns` + tenantDeleteExample = ` kubectl minio tenant delete --name tenant1 --namespace tenant1-ns` ) -type deleteCmd struct { +type tenantDeleteCmd struct { out io.Writer errOut io.Writer name string ns string } -func newDeleteCmd(out io.Writer, errOut io.Writer) *cobra.Command { - c := &deleteCmd{out: out, errOut: errOut} +func newTenantDeleteCmd(out io.Writer, errOut io.Writer) *cobra.Command { + c := &tenantDeleteCmd{out: out, errOut: errOut} cmd := &cobra.Command{ Use: "delete", @@ -67,7 +68,7 @@ func newDeleteCmd(out io.Writer, errOut io.Writer) *cobra.Command { return cmd } -func (d *deleteCmd) validate() error { +func (d *tenantDeleteCmd) validate() error { if d.name == "" { return errors.New("--name flag is required for tenant deletion") } @@ -75,15 +76,19 @@ func (d *deleteCmd) validate() error { } // run initializes local config and installs MinIO Operator to Kubernetes cluster. -func (d *deleteCmd) run(args []string) error { +func (d *tenantDeleteCmd) run(args []string) error { oclient, err := helpers.GetKubeOperatorClient() if err != nil { return err } - return deleteTenant(oclient, d) + kclient, err := helpers.GetKubeClient() + if err != nil { + return err + } + return deleteTenant(oclient, kclient, d) } -func deleteTenant(client *operatorv1.Clientset, d *deleteCmd) error { +func deleteTenant(client *operatorv1.Clientset, kclient *kubernetes.Clientset, d *tenantDeleteCmd) error { tenant, err := client.MinioV1().Tenants(d.ns).Get(context.Background(), d.name, metav1.GetOptions{}) if err != nil { return err @@ -91,7 +96,15 @@ func deleteTenant(client *operatorv1.Clientset, d *deleteCmd) error { if err := client.MinioV1().Tenants(d.ns).Delete(context.Background(), d.name, v1.DeleteOptions{}); err != nil { return err } + if err := kclient.CoreV1().Secrets(d.ns).Delete(context.Background(), tenant.Spec.CredsSecret.Name, metav1.DeleteOptions{}); err != nil { + return err + } + if err := kclient.CoreV1().Secrets(d.ns).Delete(context.Background(), tenant.Spec.Console.ConsoleSecret.Name, metav1.DeleteOptions{}); err != nil { + return err + } + fmt.Printf("Deleting MinIO Tenant %s\n", tenant.ObjectMeta.Name) - fmt.Printf("Please delete the secret %s used for MinIO Tenant credentials\n", tenant.Spec.CredsSecret.Name) + fmt.Printf("Deleting MinIO Tenant Credentials Secret %s\n", tenant.Spec.CredsSecret.Name) + fmt.Printf("Deleting MinIO Tenant Console Secret %s\n", tenant.Spec.Console.ConsoleSecret.Name) return nil } diff --git a/kubectl-minio/cmd/tenant-volumes-add.go b/kubectl-minio/cmd/tenant-expand.go similarity index 83% rename from kubectl-minio/cmd/tenant-volumes-add.go rename to kubectl-minio/cmd/tenant-expand.go index 2797a8a4cbf..443a31ed716 100644 --- a/kubectl-minio/cmd/tenant-volumes-add.go +++ b/kubectl-minio/cmd/tenant-expand.go @@ -26,9 +26,9 @@ import ( "github.com/minio/kubectl-minio/cmd/helpers" "github.com/minio/kubectl-minio/cmd/resources" - "gopkg.in/yaml.v2" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/yaml" miniov1 "github.com/minio/operator/pkg/apis/minio.min.io/v1" operatorv1 "github.com/minio/operator/pkg/client/clientset/versioned" @@ -36,26 +36,26 @@ import ( ) const ( - addDesc = ` -'add' command adds zones to a MinIO tenant` - addVolumeExample = ` kubectl minio tenant volume add --name tenant1 --servers 4 --volumes 32 --capacity 32Ti --namespace tenant1-ns` + expandDesc = ` +'expand' command adds storage capacity to a MinIO tenant` + expandExample = ` kubectl minio tenant expand --name tenant1 --servers 4 --volumes 32 --capacity 32Ti --namespace tenant1-ns` ) -type volumeAddCmd struct { +type expandCmd struct { out io.Writer errOut io.Writer output bool tenantOpts resources.TenantOptions } -func newVolumeAddCmd(out io.Writer, errOut io.Writer) *cobra.Command { - v := &volumeAddCmd{out: out, errOut: errOut} +func newTenantExpandCmd(out io.Writer, errOut io.Writer) *cobra.Command { + v := &expandCmd{out: out, errOut: errOut} cmd := &cobra.Command{ - Use: "add", - Short: "Add volumes to existing tenant", - Long: addDesc, - Example: addVolumeExample, + Use: "expand", + Short: "Add capacity to existing tenant", + Long: expandDesc, + Example: expandExample, RunE: func(cmd *cobra.Command, args []string) error { if err := v.validate(); err != nil { return err @@ -70,18 +70,17 @@ func newVolumeAddCmd(out io.Writer, errOut io.Writer) *cobra.Command { f.Int32Var(&v.tenantOpts.Volumes, "volumes", 0, "total number of volumes to add to tenant") f.StringVar(&v.tenantOpts.Capacity, "capacity", "", "total raw capacity to add to tenant, e.g. 16Ti") f.StringVarP(&v.tenantOpts.NS, "namespace", "n", helpers.DefaultNamespace, "namespace scope for this request") - f.StringVarP(&v.tenantOpts.StorageClass, "storage-class", "s", "", "storage class to be used while PVC creation") f.BoolVarP(&v.output, "output", "o", false, "dry run this command and generate requisite yaml") return cmd } -func (v *volumeAddCmd) validate() error { +func (v *expandCmd) validate() error { return v.tenantOpts.Validate() } // run initializes local config and installs MinIO Operator to Kubernetes cluster. -func (v *volumeAddCmd) run() error { +func (v *expandCmd) run() error { // Create operator client client, err := helpers.GetKubeOperatorClient() if err != nil { diff --git a/kubectl-minio/cmd/tenant-volumes-list.go b/kubectl-minio/cmd/tenant-info.go similarity index 81% rename from kubectl-minio/cmd/tenant-volumes-list.go rename to kubectl-minio/cmd/tenant-info.go index 92709b7557a..060fe8c08b4 100644 --- a/kubectl-minio/cmd/tenant-volumes-list.go +++ b/kubectl-minio/cmd/tenant-info.go @@ -34,25 +34,25 @@ import ( ) const ( - listDesc = ` -'list' command lists zones from a MinIO tenant` - listExample = ` kubectl minio tenant volume list --name tenant1 --namespace tenant1-ns` + infoDesc = ` +'info' command lists zones from a MinIO tenant` + infoExample = ` kubectl minio tenant info --name tenant1 --namespace tenant1-ns` ) -type volumeListCmd struct { +type infoCmd struct { out io.Writer errOut io.Writer name string ns string } -func newVolumeListCmd(out io.Writer, errOut io.Writer) *cobra.Command { - c := &volumeListCmd{out: out, errOut: errOut} +func newTenantInfoCmd(out io.Writer, errOut io.Writer) *cobra.Command { + c := &infoCmd{out: out, errOut: errOut} cmd := &cobra.Command{ - Use: "list", + Use: "info", Short: "List all volumes in existing tenant", - Long: listDesc, + Long: infoDesc, RunE: func(cmd *cobra.Command, args []string) error { if err := c.validate(); err != nil { return err @@ -67,7 +67,7 @@ func newVolumeListCmd(out io.Writer, errOut io.Writer) *cobra.Command { return cmd } -func (d *volumeListCmd) validate() error { +func (d *infoCmd) validate() error { if d.name == "" { return errors.New("--name flag is required for adding volumes to tenant") } @@ -75,7 +75,7 @@ func (d *volumeListCmd) validate() error { } // run initializes local config and installs MinIO Operator to Kubernetes cluster. -func (d *volumeListCmd) run() error { +func (d *infoCmd) run() error { // Create operator client oclient, err := helpers.GetKubeOperatorClient() if err != nil { @@ -88,17 +88,18 @@ func (d *volumeListCmd) run() error { return nil } -func listZonesTenant(client *operatorv1.Clientset, d *volumeListCmd) error { +func listZonesTenant(client *operatorv1.Clientset, d *infoCmd) error { tenant, err := client.MinioV1().Tenants(d.ns).Get(context.Background(), d.name, metav1.GetOptions{}) if err != nil { return err } t := table.NewWriter() t.SetOutputMirror(os.Stdout) - t.AppendHeader(table.Row{"Zone", "Servers", "Volumes Per Server", "Capacity Per Volume"}) + t.AppendHeader(table.Row{"Zone", "Servers", "Volumes Per Server", "Capacity Per Volume", "Version"}) for i, z := range tenant.Spec.Zones { t.AppendRow(table.Row{i, z.Servers, z.VolumesPerServer, z.VolumeClaimTemplate.Spec.Resources.Requests.Storage().String()}) } + t.AppendFooter(table.Row{"Version", tenant.Spec.Image}) t.SetAlign([]text.Align{text.AlignRight, text.AlignRight, text.AlignRight, text.AlignRight}) t.Render() return nil diff --git a/kubectl-minio/cmd/tenant-upgrade.go b/kubectl-minio/cmd/tenant-upgrade.go index a11943382fd..3c8289c3841 100644 --- a/kubectl-minio/cmd/tenant-upgrade.go +++ b/kubectl-minio/cmd/tenant-upgrade.go @@ -30,9 +30,9 @@ import ( miniov1 "github.com/minio/operator/pkg/apis/minio.min.io/v1" operatorv1 "github.com/minio/operator/pkg/client/clientset/versioned" "github.com/spf13/cobra" - "gopkg.in/yaml.v2" corev1 "k8s.io/api/core/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/yaml" ) const ( @@ -48,7 +48,7 @@ type upgradeCmd struct { tenantOpts resources.TenantOptions } -func newUpgradeCmd(out io.Writer, errOut io.Writer) *cobra.Command { +func newTenantUpgradeCmd(out io.Writer, errOut io.Writer) *cobra.Command { c := &upgradeCmd{out: out, errOut: errOut} cmd := &cobra.Command{ @@ -67,7 +67,6 @@ func newUpgradeCmd(out io.Writer, errOut io.Writer) *cobra.Command { f.StringVar(&c.tenantOpts.Name, "name", "", "name of the MinIO tenant to upgrade") f.StringVarP(&c.tenantOpts.Image, "image", "i", helpers.DefaultTenantImage, "image to which tenant is to be upgraded") f.StringVarP(&c.tenantOpts.NS, "namespace", "n", helpers.DefaultNamespace, "namespace scope for this request") - f.StringVar(&c.tenantOpts.ImagePullSecret, "image-pull-secrets", "", "image pull secret to be used for pulling MinIO image") f.BoolVarP(&c.output, "output", "o", false, "dry run this command and generate requisite yaml") return cmd diff --git a/kubectl-minio/cmd/tenant-volumes.go b/kubectl-minio/cmd/tenant-volumes.go deleted file mode 100644 index 24a5d3331e9..00000000000 --- a/kubectl-minio/cmd/tenant-volumes.go +++ /dev/null @@ -1,43 +0,0 @@ -/* - * This file is part of MinIO Operator - * Copyright (C) 2020, MinIO, Inc. - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see - * - */ - -package cmd - -import ( - "io" - - "github.com/spf13/cobra" -) - -const ( - volumeDesc = ` -'volume' is the top level command for managing a tenant's zones.` -) - -func newVolumeCmd(out io.Writer, errOut io.Writer) *cobra.Command { - cmd := &cobra.Command{ - Use: "volume", - Short: "Manage tenant volumes", - Long: volumeDesc, - } - - cmd.AddCommand(newVolumeAddCmd(cmd.OutOrStdout(), cmd.ErrOrStderr())) - cmd.AddCommand(newVolumeListCmd(cmd.OutOrStdout(), cmd.ErrOrStderr())) - - return cmd -} diff --git a/kubectl-minio/cmd/tenant.go b/kubectl-minio/cmd/tenant.go index 8e2451c9a32..3092894b76d 100644 --- a/kubectl-minio/cmd/tenant.go +++ b/kubectl-minio/cmd/tenant.go @@ -55,10 +55,11 @@ func newTenantCmd(out io.Writer, errOut io.Writer) *cobra.Command { }, } - cmd.AddCommand(newCreateCmd(cmd.OutOrStdout(), cmd.ErrOrStderr())) - cmd.AddCommand(newUpgradeCmd(cmd.OutOrStdout(), cmd.ErrOrStderr())) - cmd.AddCommand(newVolumeCmd(cmd.OutOrStdout(), cmd.ErrOrStderr())) - cmd.AddCommand(newDeleteCmd(cmd.OutOrStdout(), cmd.ErrOrStderr())) + cmd.AddCommand(newTenantCreateCmd(cmd.OutOrStdout(), cmd.ErrOrStderr())) + cmd.AddCommand(newTenantExpandCmd(cmd.OutOrStdout(), cmd.ErrOrStderr())) + cmd.AddCommand(newTenantInfoCmd(cmd.OutOrStdout(), cmd.ErrOrStderr())) + cmd.AddCommand(newTenantUpgradeCmd(cmd.OutOrStdout(), cmd.ErrOrStderr())) + cmd.AddCommand(newTenantDeleteCmd(cmd.OutOrStdout(), cmd.ErrOrStderr())) return cmd } diff --git a/kubectl-minio/go.mod b/kubectl-minio/go.mod index de9b3823aa7..a0a7732ec60 100644 --- a/kubectl-minio/go.mod +++ b/kubectl-minio/go.mod @@ -7,6 +7,7 @@ require ( github.com/go-openapi/errors v0.19.6 // indirect github.com/go-openapi/strfmt v0.19.5 // indirect github.com/golang/protobuf v1.4.2 // indirect + github.com/google/uuid v1.1.1 github.com/googleapis/gnostic v0.4.0 // indirect github.com/imdario/mergo v0.3.10 // indirect github.com/jedib0t/go-pretty v4.3.0+incompatible @@ -28,7 +29,6 @@ require ( golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e // indirect google.golang.org/appengine v1.6.6 // indirect google.golang.org/protobuf v1.25.0 // indirect - gopkg.in/yaml.v2 v2.3.0 gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect k8s.io/api v0.18.6 k8s.io/apiextensions-apiserver v0.18.6 @@ -36,4 +36,5 @@ require ( k8s.io/cli-runtime v0.18.6 k8s.io/client-go v0.18.6 k8s.io/utils v0.0.0-20200729134348-d5654de09c73 // indirect + sigs.k8s.io/yaml v1.2.0 )