in cmd/kops/create_cluster.go [460:825]
func RunCreateCluster(ctx context.Context, f *util.Factory, out io.Writer, c *CreateClusterOptions) error {
isDryrun := false
// direct requires --yes (others do not, because they don't make changes)
targetName := c.Target
if c.Target == cloudup.TargetDirect {
if !c.Yes {
isDryrun = true
targetName = cloudup.TargetDryRun
}
}
if c.Target == cloudup.TargetDryRun {
isDryrun = true
targetName = cloudup.TargetDryRun
}
if c.DryRun && c.Output == "" {
return fmt.Errorf("unable to execute --dry-run without setting --output")
}
// TODO: Reuse rootCommand stateStore logic?
if c.OutDir == "" {
if c.Target == cloudup.TargetTerraform {
c.OutDir = "out/terraform"
} else if c.Target == cloudup.TargetCloudformation {
c.OutDir = "out/cloudformation"
} else {
c.OutDir = "out"
}
}
clientset, err := f.Clientset()
if err != nil {
return err
}
if c.ClusterName == "" {
return fmt.Errorf("--name is required")
}
{
cluster, err := clientset.GetCluster(ctx, c.ClusterName)
if err != nil {
if apierrors.IsNotFound(err) {
cluster = nil
} else {
return err
}
}
if cluster != nil {
return fmt.Errorf("cluster %q already exists; use 'kops update cluster' to apply changes", c.ClusterName)
}
}
if c.OpenstackNetworkID != "" {
c.NetworkID = c.OpenstackNetworkID
}
clusterResult, err := cloudup.NewCluster(&c.NewClusterOptions, clientset)
if err != nil {
return err
}
cluster := clusterResult.Cluster
instanceGroups := clusterResult.InstanceGroups
var masters []*api.InstanceGroup
var nodes []*api.InstanceGroup
for _, ig := range instanceGroups {
switch ig.Spec.Role {
case api.InstanceGroupRoleMaster:
masters = append(masters, ig)
case api.InstanceGroupRoleNode:
nodes = append(nodes, ig)
}
}
cloudLabels, err := parseCloudLabels(c.CloudLabels)
if err != nil {
return fmt.Errorf("error parsing global cloud labels: %v", err)
}
if len(cloudLabels) != 0 {
cluster.Spec.CloudLabels = cloudLabels
}
if c.NodeSize != "" {
for _, group := range nodes {
group.Spec.MachineType = c.NodeSize
}
}
if c.Image != "" {
for _, group := range instanceGroups {
group.Spec.Image = c.Image
}
}
if c.MasterImage != "" {
for _, group := range masters {
group.Spec.Image = c.MasterImage
}
}
if c.NodeImage != "" {
for _, group := range nodes {
group.Spec.Image = c.NodeImage
}
}
if c.AssociatePublicIP != nil {
for _, group := range instanceGroups {
group.Spec.AssociatePublicIP = c.AssociatePublicIP
}
}
if c.MasterTenancy != "" {
for _, group := range masters {
group.Spec.Tenancy = c.MasterTenancy
}
}
if c.NodeTenancy != "" {
for _, group := range nodes {
group.Spec.Tenancy = c.NodeTenancy
}
}
if len(c.NodeSecurityGroups) > 0 {
for _, group := range nodes {
group.Spec.AdditionalSecurityGroups = c.NodeSecurityGroups
}
}
if len(c.MasterSecurityGroups) > 0 {
for _, group := range masters {
group.Spec.AdditionalSecurityGroups = c.MasterSecurityGroups
}
}
if c.MasterSize != "" {
for _, group := range masters {
group.Spec.MachineType = c.MasterSize
}
}
if c.MasterVolumeSize != 0 {
for _, group := range masters {
group.Spec.RootVolumeSize = fi.Int32(c.MasterVolumeSize)
}
}
if c.NodeVolumeSize != 0 {
for _, group := range nodes {
group.Spec.RootVolumeSize = fi.Int32(c.NodeVolumeSize)
}
}
if c.DNSZone != "" {
cluster.Spec.DNSZone = c.DNSZone
}
if c.ContainerRuntime != "" {
cluster.Spec.ContainerRuntime = c.ContainerRuntime
}
if c.NetworkCIDR != "" {
cluster.Spec.NetworkCIDR = c.NetworkCIDR
}
if c.DisableSubnetTags {
cluster.Spec.TagSubnets = fi.Bool(false)
}
if c.MasterPublicName != "" {
cluster.Spec.MasterPublicName = c.MasterPublicName
}
if err := commands.UnsetClusterFields(c.Unsets, cluster); err != nil {
return err
}
if err := commands.SetClusterFields(c.Sets, cluster); err != nil {
return err
}
cloud, err := cloudup.BuildCloud(cluster)
if err != nil {
return err
}
err = cloudup.PerformAssignments(cluster, cloud)
if err != nil {
return fmt.Errorf("error populating configuration: %v", err)
}
strict := false
err = validation.DeepValidate(cluster, instanceGroups, strict, nil)
if err != nil {
return err
}
assetBuilder := assets.NewAssetBuilder(cluster, false)
fullCluster, err := cloudup.PopulateClusterSpec(clientset, cluster, cloud, assetBuilder)
if err != nil {
return err
}
var fullInstanceGroups []*api.InstanceGroup
for _, group := range instanceGroups {
fullGroup, err := cloudup.PopulateInstanceGroupSpec(fullCluster, group, cloud, clusterResult.Channel)
if err != nil {
return err
}
fullGroup.AddInstanceGroupNodeLabel()
if api.CloudProviderID(cluster.Spec.CloudProvider) == api.CloudProviderGCE {
fullGroup.Spec.NodeLabels["cloud.google.com/metadata-proxy-ready"] = "true"
}
fullInstanceGroups = append(fullInstanceGroups, fullGroup)
}
kubernetesVersion, err := kopsutil.ParseKubernetesVersion(clusterResult.Cluster.Spec.KubernetesVersion)
if err != nil {
return fmt.Errorf("cannot parse KubernetesVersion %q in cluster: %w", clusterResult.Cluster.Spec.KubernetesVersion, err)
}
addons, err := wellknownoperators.CreateAddons(clusterResult.Channel, kubernetesVersion, fullCluster)
if err != nil {
return err
}
for _, p := range c.AddonPaths {
addon, err := clusteraddons.LoadClusterAddon(p)
if err != nil {
return fmt.Errorf("error loading cluster addon %s: %v", p, err)
}
addons = append(addons, addon.Objects...)
}
err = validation.DeepValidate(fullCluster, fullInstanceGroups, true, nil)
if err != nil {
return err
}
if c.DryRun {
var obj []runtime.Object
obj = append(obj, cluster)
for _, group := range fullInstanceGroups {
// Cluster name is not populated, and we need it
group.ObjectMeta.Labels = make(map[string]string)
group.ObjectMeta.Labels[api.LabelClusterName] = cluster.ObjectMeta.Name
obj = append(obj, group)
}
for _, o := range addons {
obj = append(obj, o.ToUnstructured())
}
switch c.Output {
case OutputYaml:
if err := fullOutputYAML(out, obj...); err != nil {
return fmt.Errorf("error writing cluster yaml to stdout: %v", err)
}
return nil
case OutputJSON:
if err := fullOutputJSON(out, obj...); err != nil {
return fmt.Errorf("error writing cluster json to stdout: %v", err)
}
return nil
default:
return fmt.Errorf("unsupported output type %q", c.Output)
}
}
// Note we perform as much validation as we can, before writing a bad config
err = registry.CreateClusterConfig(ctx, clientset, cluster, fullInstanceGroups, addons)
if err != nil {
return fmt.Errorf("error writing updated configuration: %v", err)
}
if len(c.SSHPublicKeys) == 0 {
autoloadSSHPublicKeys := true
switch c.CloudProvider {
case "gce", "aws":
autoloadSSHPublicKeys = false
}
if autoloadSSHPublicKeys {
// Load from default location, if found
sshPublicKeyPath := "~/.ssh/id_rsa.pub"
c.SSHPublicKeys, err = loadSSHPublicKeys(sshPublicKeyPath)
if err != nil {
// Don't wrap file-not-found
if os.IsNotExist(err) {
klog.V(2).Infof("ssh key not found at %s", sshPublicKeyPath)
} else {
return fmt.Errorf("error reading SSH key file %q: %v", sshPublicKeyPath, err)
}
}
}
}
if len(c.SSHPublicKeys) != 0 {
sshCredentialStore, err := clientset.SSHCredentialStore(cluster)
if err != nil {
return err
}
for _, data := range c.SSHPublicKeys {
err = sshCredentialStore.AddSSHPublicKey(data)
if err != nil {
return fmt.Errorf("error adding SSH public key: %v", err)
}
}
}
// Can we actually get to this if??
if targetName != "" {
if isDryrun {
fmt.Fprintf(out, "Previewing changes that will be made:\n\n")
}
// TODO: Maybe just embed UpdateClusterOptions in CreateClusterOptions?
updateClusterOptions := &UpdateClusterOptions{}
updateClusterOptions.InitDefaults()
updateClusterOptions.Yes = c.Yes
updateClusterOptions.Target = c.Target
updateClusterOptions.OutDir = c.OutDir
updateClusterOptions.admin = kubeconfig.DefaultKubecfgAdminLifetime
updateClusterOptions.ClusterName = cluster.Name
updateClusterOptions.CreateKubecfg = true
// SSHPublicKey has already been mapped
updateClusterOptions.SSHPublicKey = ""
_, err := RunUpdateCluster(ctx, f, out, updateClusterOptions)
if err != nil {
return err
}
if isDryrun {
var sb bytes.Buffer
fmt.Fprintf(&sb, "\n")
fmt.Fprintf(&sb, "Cluster configuration has been created.\n")
fmt.Fprintf(&sb, "\n")
fmt.Fprintf(&sb, "Suggestions:\n")
fmt.Fprintf(&sb, " * list clusters with: kops get cluster\n")
fmt.Fprintf(&sb, " * edit this cluster with: kops edit cluster %s\n", cluster.Name)
if len(nodes) > 0 {
fmt.Fprintf(&sb, " * edit your node instance group: kops edit ig --name=%s %s\n", cluster.Name, nodes[0].ObjectMeta.Name)
}
if len(masters) > 0 {
fmt.Fprintf(&sb, " * edit your master instance group: kops edit ig --name=%s %s\n", cluster.Name, masters[0].ObjectMeta.Name)
}
fmt.Fprintf(&sb, "\n")
fmt.Fprintf(&sb, "Finally configure your cluster with: kops update cluster --name %s --yes --admin\n", cluster.Name)
fmt.Fprintf(&sb, "\n")
_, err := out.Write(sb.Bytes())
if err != nil {
return fmt.Errorf("error writing to output: %v", err)
}
}
}
return nil
}