diff --git a/cmd/kops/BUILD.bazel b/cmd/kops/BUILD.bazel index 4edca6c808..6125400d59 100644 --- a/cmd/kops/BUILD.bazel +++ b/cmd/kops/BUILD.bazel @@ -5,7 +5,7 @@ go_library( srcs = [ "create.go", "create_cluster.go", - "create_ig.go", + "create_instancegroup.go", "create_keypair.go", "create_secret.go", "create_secret_cilium_encryptionconfig.go", @@ -103,6 +103,7 @@ go_library( "//util/pkg/vfs:go_default_library", "//vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/cli:go_default_library", "//vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector:go_default_library", + "//vendor/github.com/aws/aws-sdk-go/service/ec2:go_default_library", "//vendor/github.com/blang/semver/v4:go_default_library", "//vendor/github.com/google/go-containerregistry/pkg/name:go_default_library", "//vendor/github.com/google/go-containerregistry/pkg/v1/remote:go_default_library", diff --git a/cmd/kops/create_cluster.go b/cmd/kops/create_cluster.go index 726601bda0..857879fb95 100644 --- a/cmd/kops/create_cluster.go +++ b/cmd/kops/create_cluster.go @@ -26,6 +26,7 @@ import ( "os" "strings" + "github.com/aws/aws-sdk-go/service/ec2" "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/remote" "github.com/spf13/cobra" @@ -313,7 +314,7 @@ func NewCmdCreateCluster(f *util.Factory, out io.Writer) *cobra.Command { cmd.RegisterFlagCompletionFunc("channel", completeChannel) // Network topology - cmd.Flags().StringVarP(&options.Topology, "topology", "t", options.Topology, "Network topology for the cluster: public or private.") + cmd.Flags().StringVarP(&options.Topology, "topology", "t", options.Topology, "Network topology for the cluster: public or private") cmd.RegisterFlagCompletionFunc("topology", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return []string{api.TopologyPublic, api.TopologyPrivate}, cobra.ShellCompDirectiveNoFileComp }) @@ -325,7 +326,7 @@ func NewCmdCreateCluster(f *util.Factory, out io.Writer) *cobra.Command { }) // DNS - cmd.Flags().StringVar(&options.DNSType, "dns", options.DNSType, "DNS type to use: public or private.") + cmd.Flags().StringVar(&options.DNSType, "dns", options.DNSType, "DNS type to use: public or private") cmd.RegisterFlagCompletionFunc("dns", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return []string{"public", "private"}, cobra.ShellCompDirectiveNoFileComp }) @@ -340,55 +341,90 @@ func NewCmdCreateCluster(f *util.Factory, out io.Writer) *cobra.Command { }) // Master and Node Tenancy - cmd.Flags().StringVar(&options.MasterTenancy, "master-tenancy", options.MasterTenancy, "The tenancy of the master group on AWS. Can either be default or dedicated.") - cmd.Flags().StringVar(&options.NodeTenancy, "node-tenancy", options.NodeTenancy, "The tenancy of the node group on AWS. Can be either default or dedicated.") + cmd.Flags().StringVar(&options.MasterTenancy, "master-tenancy", options.MasterTenancy, "Tenancy of the master group (AWS only): default or dedicated") + cmd.RegisterFlagCompletionFunc("master-tenancy", completeTenancy) + cmd.Flags().StringVar(&options.NodeTenancy, "node-tenancy", options.NodeTenancy, "Tenancy of the node group (AWS only): default or dedicated") + cmd.RegisterFlagCompletionFunc("node-tenancy", completeTenancy) - cmd.Flags().StringVar(&options.APILoadBalancerClass, "api-loadbalancer-class", options.APILoadBalancerClass, "Currently only supported in AWS. Sets the API loadbalancer class to either 'classic' or 'network'") - cmd.Flags().StringVar(&options.APILoadBalancerType, "api-loadbalancer-type", options.APILoadBalancerType, "Sets the API loadbalancer type to either 'public' or 'internal'") - cmd.Flags().StringVar(&options.APISSLCertificate, "api-ssl-certificate", options.APISSLCertificate, "Currently only supported in AWS. Sets the ARN of the SSL Certificate to use for the API server loadbalancer.") + cmd.Flags().StringVar(&options.APILoadBalancerClass, "api-loadbalancer-class", options.APILoadBalancerClass, "Class of loadbalancer for the Kubernetes API (AWS only): classic or network") + cmd.RegisterFlagCompletionFunc("api-loadbalancer-class", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return []string{"classic", "network"}, cobra.ShellCompDirectiveNoFileComp + }) + cmd.Flags().StringVar(&options.APILoadBalancerType, "api-loadbalancer-type", options.APILoadBalancerType, "Type of loadbalancer for the Kubernetes API: public or internal") + cmd.RegisterFlagCompletionFunc("api-loadbalancer-type", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return []string{"public", "internal"}, cobra.ShellCompDirectiveNoFileComp + }) + cmd.Flags().StringVar(&options.APISSLCertificate, "api-ssl-certificate", options.APISSLCertificate, "ARN of the SSL Certificate to use for the Kubernetes API loadbalancer (AWS only)") + cmd.RegisterFlagCompletionFunc("api-ssl-certificate", completeSSLCertificate) // Allow custom public master name - cmd.Flags().StringVar(&options.MasterPublicName, "master-public-name", options.MasterPublicName, "Sets the public master public name") + cmd.Flags().StringVar(&options.MasterPublicName, "master-public-name", options.MasterPublicName, "Domain name of the public Kubernetes API") + cmd.RegisterFlagCompletionFunc("master-public-name", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return nil, cobra.ShellCompDirectiveNoFileComp + }) // DryRun mode that will print YAML or JSON cmd.Flags().BoolVar(&options.DryRun, "dry-run", options.DryRun, "If true, only print the object that would be sent, without sending it. This flag can be used to create a cluster YAML or JSON manifest.") - cmd.Flags().StringVarP(&options.Output, "output", "o", options.Output, "Output format. One of json|yaml. Used with the --dry-run flag.") + cmd.Flags().StringVarP(&options.Output, "output", "o", options.Output, "Output format. One of json or yaml. Used with the --dry-run flag.") + cmd.RegisterFlagCompletionFunc("output", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return []string{"json", "yaml"}, cobra.ShellCompDirectiveNoFileComp + }) if featureflag.SpecOverrideFlag.Enabled() { cmd.Flags().StringSliceVar(&options.Sets, "set", options.Sets, "Directly set values in the spec") + cmd.RegisterFlagCompletionFunc("set", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return nil, cobra.ShellCompDirectiveNoFileComp + }) cmd.Flags().StringSliceVar(&options.Unsets, "unset", options.Unsets, "Directly unset values in the spec") + cmd.RegisterFlagCompletionFunc("unset", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return nil, cobra.ShellCompDirectiveNoFileComp + }) } // GCE flags cmd.Flags().StringVar(&options.Project, "project", options.Project, "Project to use (must be set on GCE)") + cmd.RegisterFlagCompletionFunc("project", completeProject) cmd.Flags().StringVar(&options.GCEServiceAccount, "gce-service-account", options.GCEServiceAccount, "Service account with which the GCE VM runs. Warning: if not set, VMs will run as default compute service account.") + cmd.RegisterFlagCompletionFunc("gce-service-account", completeGCEServiceAccount) if featureflag.Azure.Enabled() { - cmd.Flags().StringVar(&options.AzureSubscriptionID, "azure-subscription-id", options.AzureSubscriptionID, "Azure subscription where the cluster is created.") + cmd.Flags().StringVar(&options.AzureSubscriptionID, "azure-subscription-id", options.AzureSubscriptionID, "Azure subscription where the cluster is created") + cmd.RegisterFlagCompletionFunc("azure-subscription-id", completeAzureSubscriptionID) cmd.Flags().StringVar(&options.AzureTenantID, "azure-tenant-id", options.AzureTenantID, "Azure tenant where the cluster is created.") + cmd.RegisterFlagCompletionFunc("azure-tenant-id", completeAzureTenantID) cmd.Flags().StringVar(&options.AzureResourceGroupName, "azure-resource-group-name", options.AzureResourceGroupName, "Azure resource group name where the cluster is created. The resource group will be created if it doesn't already exist. Defaults to the cluster name.") + cmd.RegisterFlagCompletionFunc("azure-resource-group-name", completeAzureResourceGroupName) cmd.Flags().StringVar(&options.AzureRouteTableName, "azure-route-table-name", options.AzureRouteTableName, "Azure route table name where the cluster is created.") + cmd.RegisterFlagCompletionFunc("azure-route-table-name", completeAzureRouteTableName) cmd.Flags().StringVar(&options.AzureAdminUser, "azure-admin-user", options.AzureAdminUser, "Azure admin user of VM ScaleSet.") + cmd.RegisterFlagCompletionFunc("azure-admin-user", completeAzureAdminUsers) } if featureflag.Spotinst.Enabled() { // Spotinst flags - cmd.Flags().StringVar(&options.SpotinstProduct, "spotinst-product", options.SpotinstProduct, "Set the product description (valid values: Linux/UNIX, Linux/UNIX (Amazon VPC), Windows and Windows (Amazon VPC))") - cmd.Flags().StringVar(&options.SpotinstOrientation, "spotinst-orientation", options.SpotinstOrientation, "Set the prediction strategy (valid values: balanced, cost, equal-distribution and availability)") + cmd.Flags().StringVar(&options.SpotinstProduct, "spotinst-product", options.SpotinstProduct, "Product description (valid values: Linux/UNIX, Linux/UNIX (Amazon VPC), Windows and Windows (Amazon VPC))") + cmd.RegisterFlagCompletionFunc("spotinst-product", completeSpotinstProduct) + cmd.Flags().StringVar(&options.SpotinstOrientation, "spotinst-orientation", options.SpotinstOrientation, "Prediction strategy (valid values: balanced, cost, equal-distribution and availability)") + cmd.RegisterFlagCompletionFunc("spotinst-orientation", completeSpotinstOrientation) } if featureflag.APIServerNodes.Enabled() { - cmd.Flags().Int32Var(&options.APIServerCount, "api-server-count", options.APIServerCount, "Set number of API server nodes. Defaults to 0.") + cmd.Flags().Int32Var(&options.APIServerCount, "api-server-count", options.APIServerCount, "Number of API server nodes. Defaults to 0.") } // Openstack flags - cmd.Flags().StringVar(&options.OpenstackExternalNet, "os-ext-net", options.OpenstackExternalNet, "The name of the external network to use with the openstack router") - cmd.Flags().StringVar(&options.OpenstackExternalSubnet, "os-ext-subnet", options.OpenstackExternalSubnet, "The name of the external floating subnet to use with the openstack router") - cmd.Flags().StringVar(&options.OpenstackLBSubnet, "os-lb-floating-subnet", options.OpenstackLBSubnet, "The name of the external subnet to use with the kubernetes api") - cmd.Flags().BoolVar(&options.OpenstackStorageIgnoreAZ, "os-kubelet-ignore-az", options.OpenstackStorageIgnoreAZ, "If true kubernetes may attach volumes across availability zones") - cmd.Flags().BoolVar(&options.OpenstackLBOctavia, "os-octavia", options.OpenstackLBOctavia, "If true octavia loadbalancer api will be used") + cmd.Flags().StringVar(&options.OpenstackExternalNet, "os-ext-net", options.OpenstackExternalNet, "External network to use with the openstack router") + cmd.RegisterFlagCompletionFunc("os-ext-net", completeOpenstackExternalNet) + cmd.Flags().StringVar(&options.OpenstackExternalSubnet, "os-ext-subnet", options.OpenstackExternalSubnet, "External floating subnet to use with the openstack router") + cmd.RegisterFlagCompletionFunc("os-ext-subnet", completeOpenstackExternalSubnet) + cmd.Flags().StringVar(&options.OpenstackLBSubnet, "os-lb-floating-subnet", options.OpenstackLBSubnet, "External subnet to use with the kubernetes api") + cmd.RegisterFlagCompletionFunc("os-lb-floating-subnet", completeOpenstackLBSubnet) + cmd.Flags().BoolVar(&options.OpenstackStorageIgnoreAZ, "os-kubelet-ignore-az", options.OpenstackStorageIgnoreAZ, "Attach volumes across availability zones") + cmd.Flags().BoolVar(&options.OpenstackLBOctavia, "os-octavia", options.OpenstackLBOctavia, "Use octavia loadbalancer API") cmd.Flags().StringVar(&options.OpenstackDNSServers, "os-dns-servers", options.OpenstackDNSServers, "comma separated list of DNS Servers which is used in network") - cmd.Flags().StringVar(&options.OpenstackNetworkID, "os-network", options.OpenstackNetworkID, "The ID of the existing OpenStack network to use") + cmd.RegisterFlagCompletionFunc("os-dns-servers", completeOpenstackDNSServers) + cmd.Flags().StringVar(&options.OpenstackNetworkID, "os-network", options.OpenstackNetworkID, "ID of the existing OpenStack network to use") + cmd.RegisterFlagCompletionFunc("os-network", completeOpenstackNetworkID) cmd.Flags().SetNormalizeFunc(func(f *pflag.FlagSet, name string) pflag.NormalizedName { switch name { @@ -931,3 +967,82 @@ func completeSecurityGroup(options *CreateClusterOptions) func(cmd *cobra.Comman return nil, cobra.ShellCompDirectiveNoFileComp } } + +func completeTenancy(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return ec2.Tenancy_Values(), cobra.ShellCompDirectiveNoFileComp +} + +func completeSSLCertificate(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + // TODO call into cloud provider(s) to get list of certificates + return nil, cobra.ShellCompDirectiveNoFileComp +} + +func completeProject(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + // TODO call into cloud provider(s) to get list of projects + return nil, cobra.ShellCompDirectiveNoFileComp +} + +func completeGCEServiceAccount(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + // TODO call into cloud provider to get list of service accounts + return nil, cobra.ShellCompDirectiveNoFileComp +} + +func completeAzureSubscriptionID(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + // TODO call into cloud provider to get list of subscription IDs + return nil, cobra.ShellCompDirectiveNoFileComp +} + +func completeAzureTenantID(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + // TODO call into cloud provider to get list of tenant IDs + return nil, cobra.ShellCompDirectiveNoFileComp +} + +func completeAzureResourceGroupName(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + // TODO call into cloud provider to get list of resource group names + return nil, cobra.ShellCompDirectiveNoFileComp +} + +func completeAzureRouteTableName(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + // TODO call into cloud provider to get list of route table names + return nil, cobra.ShellCompDirectiveNoFileComp +} + +func completeAzureAdminUsers(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + // TODO call into cloud provider to get list of admin users + return nil, cobra.ShellCompDirectiveNoFileComp +} + +func completeSpotinstProduct(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + // TODO call into cloud provider to get list of products + return nil, cobra.ShellCompDirectiveNoFileComp +} + +func completeSpotinstOrientation(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + // TODO call into cloud provider to get list of orientations + return nil, cobra.ShellCompDirectiveNoFileComp +} + +func completeOpenstackExternalNet(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + // TODO call into cloud provider to get list of external networks + return nil, cobra.ShellCompDirectiveNoFileComp +} + +func completeOpenstackExternalSubnet(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + // TODO call into cloud provider to get list of external floating subnets + return nil, cobra.ShellCompDirectiveNoFileComp +} + +func completeOpenstackLBSubnet(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + // TODO call into cloud provider to get list of external subnets + return nil, cobra.ShellCompDirectiveNoFileComp +} + +func completeOpenstackDNSServers(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + // TODO call into cloud provider to get list of DNS servers + return nil, cobra.ShellCompDirectiveNoFileComp +} + +func completeOpenstackNetworkID(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + // TODO call into cloud provider to get list of network IDs + return nil, cobra.ShellCompDirectiveNoFileComp +} diff --git a/cmd/kops/create_ig.go b/cmd/kops/create_instancegroup.go similarity index 58% rename from cmd/kops/create_ig.go rename to cmd/kops/create_instancegroup.go index 55fcf1ed40..1ad3bd7d84 100644 --- a/cmd/kops/create_ig.go +++ b/cmd/kops/create_instancegroup.go @@ -28,10 +28,13 @@ import ( "github.com/spf13/cobra" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/sets" "k8s.io/klog/v2" "k8s.io/kops/cmd/kops/util" kopsapi "k8s.io/kops/pkg/apis/kops" "k8s.io/kops/pkg/apis/kops/validation" + "k8s.io/kops/pkg/commands/commandutils" + "k8s.io/kops/pkg/featureflag" "k8s.io/kops/pkg/kopscodecs" "k8s.io/kops/pkg/try" "k8s.io/kops/upup/pkg/fi/cloudup" @@ -41,8 +44,10 @@ import ( ) type CreateInstanceGroupOptions struct { - Role string - Subnets []string + ClusterName string + InstanceGroupName string + Role string + Subnets []string // DryRun mode output an ig manifest of Output type. DryRun bool // Output type during a DryRun @@ -52,7 +57,7 @@ type CreateInstanceGroupOptions struct { } var ( - createIgLong = templates.LongDesc(i18n.T(` + createInstanceGroupLong = templates.LongDesc(i18n.T(` Create an InstanceGroup configuration. An InstanceGroup is a group of similar virtual machines. @@ -60,18 +65,18 @@ var ( The Role of an InstanceGroup defines whether machines will act as a Kubernetes master or node.`)) - createIgExample = templates.Examples(i18n.T(` + createInstanceGroupExample = templates.Examples(i18n.T(` # Create an instancegroup for the k8s-cluster.example.com cluster. - kops create ig --name=k8s-cluster.example.com node-example \ + kops create instancegroup --name=k8s-cluster.example.com node-example \ --role node --subnet my-subnet-name,my-other-subnet-name # Create a YAML manifest for an instancegroup for the k8s-cluster.example.com cluster. - kops create ig --name=k8s-cluster.example.com node-example \ + kops create instancegroup --name=k8s-cluster.example.com node-example \ --role node --subnet my-subnet-name --dry-run -oyaml `)) - createIgShort = i18n.T(`Create an instancegroup.`) + createInstanceGroupShort = i18n.T(`Create an instancegroup.`) ) // NewCmdCreateInstanceGroup create a new cobra command object for creating a instancegroup. @@ -82,48 +87,71 @@ func NewCmdCreateInstanceGroup(f *util.Factory, out io.Writer) *cobra.Command { } cmd := &cobra.Command{ - Use: "instancegroup", + Use: "instancegroup INSTANCE_GROUP", Aliases: []string{"instancegroups", "ig"}, - Short: createIgShort, - Long: createIgLong, - Example: createIgExample, - Run: func(cmd *cobra.Command, args []string) { - ctx := context.TODO() - err := RunCreateInstanceGroup(ctx, f, cmd, args, os.Stdout, options) - if err != nil { - exitWithError(err) + Short: createInstanceGroupShort, + Long: createInstanceGroupLong, + Example: createInstanceGroupExample, + Args: func(cmd *cobra.Command, args []string) error { + options.ClusterName = rootCommand.ClusterName(true) + + if options.ClusterName == "" { + return fmt.Errorf("--name is required") } + + if len(args) == 0 { + return fmt.Errorf("must specify name of instance group to create") + } + + options.InstanceGroupName = args[0] + + if len(args) != 1 { + return fmt.Errorf("can only create one instance group at a time") + } + + return nil + }, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + commandutils.ConfigureKlogForCompletion() + if len(args) == 1 && rootCommand.ClusterName(false) == "" { + return []string{"--name"}, cobra.ShellCompDirectiveNoFileComp + } + return nil, cobra.ShellCompDirectiveNoFileComp + }, + RunE: func(cmd *cobra.Command, args []string) error { + return RunCreateInstanceGroup(context.TODO(), f, out, options) }, } - // TODO: Create Enum helper - or is there one in k8s already? - var allRoles []string + allRoles := make([]string, 0, len(kopsapi.AllInstanceGroupRoles)) for _, r := range kopsapi.AllInstanceGroupRoles { - allRoles = append(allRoles, string(r)) + if r == kopsapi.InstanceGroupRoleAPIServer && !featureflag.APIServerNodes.Enabled() { + continue + } + allRoles = append(allRoles, strings.ToLower(string(r))) } cmd.Flags().StringVar(&options.Role, "role", options.Role, "Type of instance group to create ("+strings.Join(allRoles, ",")+")") + cmd.RegisterFlagCompletionFunc("role", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return allRoles, cobra.ShellCompDirectiveNoFileComp + }) cmd.Flags().StringSliceVar(&options.Subnets, "subnet", options.Subnets, "Subnet in which to create instance group. One of Availability Zone like eu-west-1a or a comma-separated list of multiple Availability Zones.") + cmd.RegisterFlagCompletionFunc("subnet", completeClusterSubnet(options)) // DryRun mode that will print YAML or JSON - cmd.Flags().BoolVar(&options.DryRun, "dry-run", options.DryRun, "If true, only print the object that would be sent, without sending it. This flag can be used to create a cluster YAML or JSON manifest.") - cmd.Flags().StringVarP(&options.Output, "output", "o", options.Output, "Output format. One of json|yaml") - cmd.Flags().BoolVar(&options.Edit, "edit", options.Edit, "If true, an editor will be opened to edit default values.") + cmd.Flags().BoolVar(&options.DryRun, "dry-run", options.DryRun, "Only print the object that would be created, without created it. This flag can be used to create an instance group YAML or JSON manifest.") + cmd.Flags().StringVarP(&options.Output, "output", "o", options.Output, "Output format. One of json or yaml") + cmd.RegisterFlagCompletionFunc("output", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return []string{"json", "yaml"}, cobra.ShellCompDirectiveNoFileComp + }) + cmd.Flags().BoolVar(&options.Edit, "edit", options.Edit, "Open an editor to edit default values") return cmd } -func RunCreateInstanceGroup(ctx context.Context, f *util.Factory, cmd *cobra.Command, args []string, out io.Writer, options *CreateInstanceGroupOptions) error { - if len(args) == 0 { - return fmt.Errorf("Specify name of instance group to create") - } - if len(args) != 1 { - return fmt.Errorf("Can only create one instance group at a time!") - } - groupName := args[0] - - cluster, err := rootCommand.Cluster(ctx) +func RunCreateInstanceGroup(ctx context.Context, f *util.Factory, out io.Writer, options *CreateInstanceGroupOptions) error { + cluster, err := GetCluster(ctx, f, options.ClusterName) if err != nil { - return err + return fmt.Errorf("error getting cluster: %q: %v", options.ClusterName, err) } clientset, err := rootCommand.Clientset() @@ -136,7 +164,7 @@ func RunCreateInstanceGroup(ctx context.Context, f *util.Factory, cmd *cobra.Com klog.Warningf("%v", err) } - existing, err := clientset.InstanceGroupsFor(cluster).Get(ctx, groupName, metav1.GetOptions{}) + existing, err := clientset.InstanceGroupsFor(cluster).Get(ctx, options.InstanceGroupName, metav1.GetOptions{}) if err != nil { // We expect a NotFound error when creating the instance group if !errors.IsNotFound(err) { @@ -145,12 +173,12 @@ func RunCreateInstanceGroup(ctx context.Context, f *util.Factory, cmd *cobra.Com } if existing != nil { - return fmt.Errorf("instance group %q already exists", groupName) + return fmt.Errorf("instance group %q already exists", options.InstanceGroupName) } // Populate some defaults ig := &kopsapi.InstanceGroup{} - ig.ObjectMeta.Name = groupName + ig.ObjectMeta.Name = options.InstanceGroupName role, ok := kopsapi.ParseInstanceGroupRole(options.Role, true) if !ok { @@ -248,3 +276,36 @@ func RunCreateInstanceGroup(ctx context.Context, f *util.Factory, cmd *cobra.Com return nil } + +func completeClusterSubnet(options *CreateInstanceGroupOptions) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + commandutils.ConfigureKlogForCompletion() + ctx := context.TODO() + + cluster, _, completions, directive := GetClusterForCompletion(ctx, &rootCommand, nil) + if cluster == nil { + return completions, directive + } + + if len(args) > 1 { + return commandutils.CompletionError("too many arguments", nil) + } + + var requiredType kopsapi.SubnetType + var subnets []string + alreadySelected := sets.NewString(options.Subnets...) + for _, subnet := range cluster.Spec.Subnets { + if alreadySelected.Has(subnet.Name) { + requiredType = subnet.Type + } + } + for _, subnet := range cluster.Spec.Subnets { + if !alreadySelected.Has(subnet.Name) && subnet.Type != kopsapi.SubnetTypeUtility && + (subnet.Type == requiredType || requiredType == "") { + subnets = append(subnets, subnet.Name) + } + } + + return subnets, cobra.ShellCompDirectiveNoFileComp + } +} diff --git a/docs/cli/kops_create_cluster.md b/docs/cli/kops_create_cluster.md index 5c04f9e8f4..e2d8ca63f0 100644 --- a/docs/cli/kops_create_cluster.md +++ b/docs/cli/kops_create_cluster.md @@ -66,9 +66,9 @@ kops create cluster [CLUSTER] [flags] ``` --admin-access strings Restrict API access to this CIDR. If not set, access will not be restricted by IP. (default [0.0.0.0/0,::/0]) - --api-loadbalancer-class string Currently only supported in AWS. Sets the API loadbalancer class to either 'classic' or 'network' - --api-loadbalancer-type string Sets the API loadbalancer type to either 'public' or 'internal' - --api-ssl-certificate string Currently only supported in AWS. Sets the ARN of the SSL Certificate to use for the API server loadbalancer. + --api-loadbalancer-class string Class of loadbalancer for the Kubernetes API (AWS only): classic or network + --api-loadbalancer-type string Type of loadbalancer for the Kubernetes API: public or internal + --api-ssl-certificate string ARN of the SSL Certificate to use for the Kubernetes API loadbalancer (AWS only) --associate-public-ip Specify --associate-public-ip=[true|false] to enable/disable association of public IP for master ASG and nodes. Default is 'true'. --authorization string Authorization mode: AlwaysAllow or RBAC (default "RBAC") --bastion Enable a bastion instance group. Only applies to private topology. @@ -77,7 +77,7 @@ kops create cluster [CLUSTER] [flags] --cloud-labels string A list of key/value pairs used to tag all instance groups (for example "Owner=John Doe,Team=Some Team"). --container-runtime string Container runtime to use: containerd, docker --disable-subnet-tags Disable automatic subnet tagging - --dns string DNS type to use: public or private. (default "Public") + --dns string DNS type to use: public or private (default "Public") --dns-zone string DNS hosted zone (defaults to longest matching zone) --dry-run If true, only print the object that would be sent, without sending it. This flag can be used to create a cluster YAML or JSON manifest. --encrypt-etcd-storage Generate key in AWS KMS and use it for encrypt etcd volumes @@ -88,10 +88,10 @@ kops create cluster [CLUSTER] [flags] --kubernetes-version string Version of kubernetes to run (defaults to version in channel) --master-count int32 Number of masters. Defaults to one master per master-zone --master-image string Machine image for masters. Takes precedence over --image - --master-public-name string Sets the public master public name + --master-public-name string Domain name of the public Kubernetes API --master-security-groups strings Additional precreated security groups to add to masters. --master-size string Machine type for masters - --master-tenancy string The tenancy of the master group on AWS. Can either be default or dedicated. + --master-tenancy string Tenancy of the master group (AWS only): default or dedicated --master-volume-size int32 Instance volume size (in GB) for masters --master-zones strings Zones in which to run masters (must be an odd number) --network-cidr string Network CIDR to use @@ -100,23 +100,23 @@ kops create cluster [CLUSTER] [flags] --node-image string Machine image for worker nodes. Takes precedence over --image --node-security-groups strings Additional precreated security groups to add to worker nodes. --node-size string Machine type for worker nodes - --node-tenancy string The tenancy of the node group on AWS. Can be either default or dedicated. + --node-tenancy string Tenancy of the node group (AWS only): default or dedicated --node-volume-size int32 Instance volume size (in GB) for worker nodes --os-dns-servers string comma separated list of DNS Servers which is used in network - --os-ext-net string The name of the external network to use with the openstack router - --os-ext-subnet string The name of the external floating subnet to use with the openstack router - --os-kubelet-ignore-az If true kubernetes may attach volumes across availability zones - --os-lb-floating-subnet string The name of the external subnet to use with the kubernetes api - --os-network string The ID of the existing OpenStack network to use - --os-octavia If true octavia loadbalancer api will be used + --os-ext-net string External network to use with the openstack router + --os-ext-subnet string External floating subnet to use with the openstack router + --os-kubelet-ignore-az Attach volumes across availability zones + --os-lb-floating-subnet string External subnet to use with the kubernetes api + --os-network string ID of the existing OpenStack network to use + --os-octavia Use octavia loadbalancer API --out string Path to write any local output - -o, --output string Output format. One of json|yaml. Used with the --dry-run flag. + -o, --output string Output format. One of json or yaml. Used with the --dry-run flag. --project string Project to use (must be set on GCE) --ssh-access strings Restrict SSH access to this CIDR. If not set, uses the value of the admin-access flag. --ssh-public-key string SSH public key to use (defaults to ~/.ssh/id_rsa.pub on AWS) --subnets strings Shared subnets to use --target string Valid targets: direct, terraform, cloudformation. Set this flag to terraform if you want kOps to generate terraform (default "direct") - -t, --topology string Network topology for the cluster: public or private. (default "public") + -t, --topology string Network topology for the cluster: public or private (default "public") --utility-subnets strings Shared utility subnets to use --vpc string Shared VPC to use -y, --yes Specify --yes to immediately create the cluster diff --git a/docs/cli/kops_create_instancegroup.md b/docs/cli/kops_create_instancegroup.md index af81e3a867..576539c824 100644 --- a/docs/cli/kops_create_instancegroup.md +++ b/docs/cli/kops_create_instancegroup.md @@ -14,29 +14,29 @@ Create an InstanceGroup configuration. The Role of an InstanceGroup defines whether machines will act as a Kubernetes master or node. ``` -kops create instancegroup [flags] +kops create instancegroup INSTANCE_GROUP [flags] ``` ### Examples ``` # Create an instancegroup for the k8s-cluster.example.com cluster. - kops create ig --name=k8s-cluster.example.com node-example \ + kops create instancegroup --name=k8s-cluster.example.com node-example \ --role node --subnet my-subnet-name,my-other-subnet-name # Create a YAML manifest for an instancegroup for the k8s-cluster.example.com cluster. - kops create ig --name=k8s-cluster.example.com node-example \ + kops create instancegroup --name=k8s-cluster.example.com node-example \ --role node --subnet my-subnet-name --dry-run -oyaml ``` ### Options ``` - --dry-run If true, only print the object that would be sent, without sending it. This flag can be used to create a cluster YAML or JSON manifest. - --edit If true, an editor will be opened to edit default values. (default true) + --dry-run Only print the object that would be created, without created it. This flag can be used to create an instance group YAML or JSON manifest. + --edit Open an editor to edit default values (default true) -h, --help help for instancegroup - -o, --output string Output format. One of json|yaml - --role string Type of instance group to create (Master,APIServer,Node,Bastion) (default "Node") + -o, --output string Output format. One of json or yaml + --role string Type of instance group to create (master,node,bastion) (default "Node") --subnet strings Subnet in which to create instance group. One of Availability Zone like eu-west-1a or a comma-separated list of multiple Availability Zones. ```