diff --git a/kubectl-minio/cmd/helpers/helpers.go b/kubectl-minio/cmd/helpers/helpers.go index 4bddaf3dfc2..29b55d01090 100644 --- a/kubectl-minio/cmd/helpers/helpers.go +++ b/kubectl-minio/cmd/helpers/helpers.go @@ -25,6 +25,7 @@ import ( "io" "os" "os/exec" + "regexp" "strings" "k8s.io/client-go/dynamic" @@ -45,6 +46,31 @@ import ( "k8s.io/client-go/tools/clientcmd" ) +var ( + validTenantName = regexp.MustCompile(`^[a-z0-9][a-z0-9\.\-]{1,61}[a-z0-9]$`) + ipAddress = regexp.MustCompile(`^(\d+\.){3}\d+$`) +) + +// CheckValidTenantName validates if input tenantname complies with expected restrictions. +func CheckValidTenantName(tenantName string) error { + if strings.TrimSpace(tenantName) == "" { + return errors.New("Tenant name cannot be empty") + } + if len(tenantName) > 63 { + return errors.New("Tenant name cannot be longer than 63 characters") + } + if ipAddress.MatchString(tenantName) { + return errors.New("Tenant name cannot be an ip address") + } + if strings.Contains(tenantName, "..") || strings.Contains(tenantName, ".-") || strings.Contains(tenantName, "-.") { + return errors.New("Tenant name contains invalid characters") + } + if !validTenantName.MatchString(tenantName) { + return errors.New("Tenant name contains invalid characters") + } + return nil +} + // GetKubeClient provides k8s client for kubeconfig func GetKubeClient(path string) (*kubernetes.Clientset, error) { loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() diff --git a/kubectl-minio/cmd/tenant-create.go b/kubectl-minio/cmd/tenant-create.go index 588c78df2f1..5bb696695dd 100644 --- a/kubectl-minio/cmd/tenant-create.go +++ b/kubectl-minio/cmd/tenant-create.go @@ -103,6 +103,10 @@ func (c *createCmd) validate(args []string) error { if args[0] == "" { return errors.New("create command requires specifying the tenant name as an argument, e.g. 'kubectl minio tenant create tenant1'") } + // Tenant name should have DNS token restrictions + if err := helpers.CheckValidTenantName(args[0]); err != nil { + return err + } c.tenantOpts.Name = args[0] c.tenantOpts.SecretName = c.tenantOpts.Name + tenantSecretSuffix return c.tenantOpts.Validate() diff --git a/kubectl-minio/cmd/tenant-delete.go b/kubectl-minio/cmd/tenant-delete.go index 88be3dd85f3..e758dcf8202 100644 --- a/kubectl-minio/cmd/tenant-delete.go +++ b/kubectl-minio/cmd/tenant-delete.go @@ -88,7 +88,8 @@ func (d *tenantDeleteCmd) validate(args []string) error { if args[0] == "" { return errors.New("provide the name of the tenant, e.g. 'kubectl minio tenant delete tenant1'") } - return nil + // Tenant name should have DNS token restrictions + return helpers.CheckValidTenantName(args[0]) } // run initializes local config and installs MinIO Operator to Kubernetes cluster. diff --git a/kubectl-minio/cmd/tenant-expand.go b/kubectl-minio/cmd/tenant-expand.go index 9b08d9d30c2..137ba774e5b 100644 --- a/kubectl-minio/cmd/tenant-expand.go +++ b/kubectl-minio/cmd/tenant-expand.go @@ -96,6 +96,11 @@ func (v *expandCmd) validate(args []string) error { if args[0] == "" { return errors.New("provide the name of the tenant, e.g. 'kubectl minio tenant expand tenant1'") } + // Tenant name should have DNS token restrictions + if err := helpers.CheckValidTenantName(args[0]); err != nil { + return err + } + v.tenantOpts.Name = args[0] return v.tenantOpts.Validate() } @@ -108,6 +113,18 @@ func (v *expandCmd) run() error { return err } + if v.tenantOpts.NS == "" || v.tenantOpts.NS == helpers.DefaultNamespace { + tenants, err := client.MinioV2().Tenants("").List(context.Background(), metav1.ListOptions{}) + if err != nil { + return err + } + for _, tenant := range tenants.Items { + if tenant.Name == v.tenantOpts.Name { + v.tenantOpts.NS = tenant.ObjectMeta.Namespace + } + } + } + t, err := client.MinioV2().Tenants(v.tenantOpts.NS).Get(context.Background(), v.tenantOpts.Name, metav1.GetOptions{}) if err != nil { return err diff --git a/kubectl-minio/cmd/tenant-info.go b/kubectl-minio/cmd/tenant-info.go index fc63f2744a4..59617c528d0 100644 --- a/kubectl-minio/cmd/tenant-info.go +++ b/kubectl-minio/cmd/tenant-info.go @@ -92,6 +92,18 @@ func (d *infoCmd) run(args []string) error { return err } + if d.ns == "" || d.ns == helpers.DefaultNamespace { + tenants, err := oclient.MinioV2().Tenants("").List(context.Background(), metav1.ListOptions{}) + if err != nil { + return err + } + for _, tenant := range tenants.Items { + if tenant.Name == args[0] { + d.ns = tenant.ObjectMeta.Namespace + } + } + } + tenant, err := oclient.MinioV2().Tenants(d.ns).Get(context.Background(), args[0], metav1.GetOptions{}) if err != nil { return err diff --git a/kubectl-minio/cmd/tenant-list.go b/kubectl-minio/cmd/tenant-list.go index 8124612de3b..c94de82b9b8 100644 --- a/kubectl-minio/cmd/tenant-list.go +++ b/kubectl-minio/cmd/tenant-list.go @@ -80,16 +80,16 @@ func (d *listCmd) run(args []string) error { return err } - tenant, err := oclient.MinioV2().Tenants("").List(context.Background(), metav1.ListOptions{}) + tenants, err := oclient.MinioV2().Tenants("").List(context.Background(), metav1.ListOptions{}) if err != nil { return err } - printTenantList(*tenant) + printTenantList(tenants) return nil } -func printTenantList(tenants miniov2.TenantList) { +func printTenantList(tenants *miniov2.TenantList) { for _, tenant := range tenants.Items { fmt.Printf(Bold(fmt.Sprintf("\nTenant '%s', Namespace '%s', Total capacity %s\n\n", tenant.Name, tenant.ObjectMeta.Namespace, helpers.TotalCapacity(tenant)))) fmt.Printf(Blue(" Current status: %s \n", tenant.Status.CurrentState)) diff --git a/kubectl-minio/cmd/tenant-report.go b/kubectl-minio/cmd/tenant-report.go index c0b1df5ba3f..071a0e931f7 100644 --- a/kubectl-minio/cmd/tenant-report.go +++ b/kubectl-minio/cmd/tenant-report.go @@ -82,7 +82,8 @@ func (d *reportCmd) validate(args []string) error { if args[0] == "" { return errors.New("provide the name of the tenant, e.g. 'kubectl minio tenant report tenant1'") } - return nil + // Tenant name should have DNS token restrictions + return helpers.CheckValidTenantName(args[0]) } // run initializes local config and installs MinIO Operator to Kubernetes cluster. @@ -98,6 +99,18 @@ func (d *reportCmd) run(args []string) error { return err } + if d.ns == "" || d.ns == helpers.DefaultNamespace { + tenants, err := oclient.MinioV2().Tenants("").List(context.Background(), metav1.ListOptions{}) + if err != nil { + return err + } + for _, tenant := range tenants.Items { + if tenant.Name == args[0] { + d.ns = tenant.ObjectMeta.Namespace + } + } + } + tenant, err := oclient.MinioV2().Tenants(d.ns).Get(context.Background(), args[0], metav1.GetOptions{}) if err != nil { return err diff --git a/kubectl-minio/cmd/tenant-upgrade.go b/kubectl-minio/cmd/tenant-upgrade.go index c251c567dc8..c743a39f74a 100644 --- a/kubectl-minio/cmd/tenant-upgrade.go +++ b/kubectl-minio/cmd/tenant-upgrade.go @@ -91,7 +91,8 @@ func (u *upgradeCmd) validate(args []string) error { if u.tenantOpts.Image == "" { return fmt.Errorf("provide the --image flag, e.g. 'kubectl minio tenant upgrade tenant1 --image %s'", helpers.DefaultTenantImage) } - return nil + // Tenant name should have DNS token restrictions + return helpers.CheckValidTenantName(args[0]) } // run initializes local config and installs MinIO Operator to Kubernetes cluster. @@ -112,6 +113,18 @@ func (u *upgradeCmd) run() error { return fmt.Errorf("Unsupported release tag, unable to apply requested update %w", err) } + if u.tenantOpts.NS == "" || u.tenantOpts.NS == helpers.DefaultNamespace { + tenants, err := client.MinioV2().Tenants("").List(context.Background(), v1.ListOptions{}) + if err != nil { + return err + } + for _, tenant := range tenants.Items { + if tenant.Name == u.tenantOpts.Name { + u.tenantOpts.NS = tenant.ObjectMeta.Namespace + } + } + } + t, err := client.MinioV2().Tenants(u.tenantOpts.NS).Get(context.Background(), u.tenantOpts.Name, v1.GetOptions{}) if err != nil { return err