Skip to content

Commit

Permalink
Schema v1alpha2
Browse files Browse the repository at this point in the history
* Zones are now subnets
* Utility subnet is no longer part of Zone
* Bastion InstanceGroup type added instead
* Etcd clusters defined in terms of InstanceGroups, not zones
* AdminAccess split into SSHAccess & APIAccess
* Dropped unused Multizone flag
  • Loading branch information
justinsb committed Dec 19, 2016
1 parent cd9febe commit fed6831
Show file tree
Hide file tree
Showing 103 changed files with 5,507 additions and 1,671 deletions.
12 changes: 12 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -244,3 +244,15 @@ channels-gocode:

examples:
go install k8s.io/kops/examples/kops-api-example/...

# -----------------------------------------------------
# api machinery regenerate

apimachinery:
#go install ./cmd/libs/go2idl/conversion-gen
~/k8s/bin/conversion-gen --input-dirs k8s.io/kops/pkg/apis/kops/v1alpha1 --v=8 --output-file-base=zz_generated.conversion
#go install github.com/ugorji/go/codec/codecgen
# codecgen works only if invoked from directory where the file is located.
#cd pkg/apis/kops/v1alpha2/ && ~/k8s/bin/codecgen -d 1234 -o types.generated.go instancegroup.go cluster.go federation.go
#cd pkg/apis/kops/v1alpha1/ && ~/k8s/bin/codecgen -d 1234 -o types.generated.go instancegroup.go cluster.go federation.go
#cd pkg/apis/kops/ && ~/k8s/bin/codecgen -d 1234 -o types.generated.go instancegroup.go cluster.go federation.go
106 changes: 63 additions & 43 deletions cmd/kops/create_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ import (
"k8s.io/kops/upup/pkg/fi/cloudup"
"k8s.io/kops/upup/pkg/fi/utils"
"k8s.io/kops/upup/pkg/kutil"
"k8s.io/kubernetes/pkg/util/sets"
)

type CreateClusterOptions struct {
Expand Down Expand Up @@ -229,20 +228,21 @@ func RunCreateCluster(f *util.Factory, out io.Writer, c *CreateClusterOptions) e
glog.V(4).Infof("networking mode=%s => %s", c.Networking, fi.DebugAsJsonString(cluster.Spec.Networking))

if c.Zones != "" {
existingZones := make(map[string]*api.ClusterZoneSpec)
for _, zone := range cluster.Spec.Zones {
existingZones[zone.Name] = zone
existingSubnets := make(map[string]*api.ClusterSubnetSpec)
for i := range cluster.Spec.Subnets {
subnet := &cluster.Spec.Subnets[i]
existingSubnets[subnet.SubnetName] = subnet
}
for _, zone := range parseZoneList(c.Zones) {
if existingZones[zone] == nil {
cluster.Spec.Zones = append(cluster.Spec.Zones, &api.ClusterZoneSpec{
Name: zone,
for _, subnetName := range parseZoneList(c.Zones) {
if existingSubnets[subnetName] == nil {
cluster.Spec.Subnets = append(cluster.Spec.Subnets, api.ClusterSubnetSpec{
SubnetName: subnetName,
})
}
}
}

if len(cluster.Spec.Zones) == 0 {
if len(cluster.Spec.Subnets) == 0 {
return fmt.Errorf("must specify at least one zone for the cluster (use --zones)")
}

Expand All @@ -255,13 +255,13 @@ func RunCreateCluster(f *util.Factory, out io.Writer, c *CreateClusterOptions) e
// We default to single-master (not HA), unless the user explicitly specifies it
// HA master is a little slower, not as well tested yet, and requires more resources
// Probably best not to make it the silent default!
for _, zone := range cluster.Spec.Zones {
for _, subnet := range cluster.Spec.Subnets {
g := &api.InstanceGroup{}
g.Spec.Role = api.InstanceGroupRoleMaster
g.Spec.Zones = []string{zone.Name}
g.Spec.Subnets = []string{subnet.SubnetName}
g.Spec.MinSize = fi.Int(1)
g.Spec.MaxSize = fi.Int(1)
g.ObjectMeta.Name = "master-" + zone.Name // Subsequent masters (if we support that) could be <zone>-1, <zone>-2
g.ObjectMeta.Name = "master-" + subnet.SubnetName // Subsequent masters (if we support that) could be <zone>-1, <zone>-2
instanceGroups = append(instanceGroups, g)
masters = append(masters, g)

Expand All @@ -272,13 +272,13 @@ func RunCreateCluster(f *util.Factory, out io.Writer, c *CreateClusterOptions) e
} else {
if len(masters) == 0 {
// Use the specified master zones (this is how the user gets HA master)
for _, zone := range parseZoneList(c.MasterZones) {
for _, subnetName := range parseZoneList(c.MasterZones) {
g := &api.InstanceGroup{}
g.Spec.Role = api.InstanceGroupRoleMaster
g.Spec.Zones = []string{zone}
g.Spec.Subnets = []string{subnetName}
g.Spec.MinSize = fi.Int(1)
g.Spec.MaxSize = fi.Int(1)
g.ObjectMeta.Name = "master-" + zone
g.ObjectMeta.Name = "master-" + subnetName
instanceGroups = append(instanceGroups, g)
masters = append(masters, g)
}
Expand All @@ -289,21 +289,38 @@ func RunCreateCluster(f *util.Factory, out io.Writer, c *CreateClusterOptions) e
}

if len(cluster.Spec.EtcdClusters) == 0 {
zones := sets.NewString()
for _, group := range masters {
for _, zone := range group.Spec.Zones {
zones.Insert(zone)
subnetMap := make(map[string]*api.ClusterSubnetSpec)
for i := range cluster.Spec.Subnets {
subnet := &cluster.Spec.Subnets[i]
subnetMap[subnet.SubnetName] = subnet
}

masterInstanceGroups := make(map[string]*api.InstanceGroup)
for _, ig := range masters {
if len(ig.Spec.Subnets) != 1 {
return fmt.Errorf("unexpected subnets for master instance group %q (expected exactly only, found %d)", ig.ObjectMeta.Name, len(ig.Spec.Subnets))
}
for _, subnetName := range ig.Spec.Subnets {
subnet := subnetMap[subnetName]
if subnet == nil {
return fmt.Errorf("cannot find subnet %q (declared in instance group %q, not found in cluster)", subnetName, ig.ObjectMeta.Name)
}

if masterInstanceGroups[subnetName] != nil {
return fmt.Errorf("found two master instance groups in subnet %q", subnetName)
}

masterInstanceGroups[subnetName] = ig
}
}
etcdZones := zones.List()

for _, etcdCluster := range cloudup.EtcdClusters {
etcd := &api.EtcdClusterSpec{}
etcd.Name = etcdCluster
for _, zone := range etcdZones {
for _, ig := range masterInstanceGroups {
m := &api.EtcdMemberSpec{}
m.Name = zone
m.Zone = fi.String(zone)
m.Name = ig.ObjectMeta.Name
m.InstanceGroup = fi.String(ig.ObjectMeta.Name)
etcd.Members = append(etcd.Members, m)
}
cluster.Spec.EtcdClusters = append(cluster.Spec.EtcdClusters, etcd)
Expand Down Expand Up @@ -380,10 +397,10 @@ func RunCreateCluster(f *util.Factory, out io.Writer, c *CreateClusterOptions) e
}

if cluster.Spec.CloudProvider == "" {
for _, zone := range cluster.Spec.Zones {
cloud, known := fi.GuessCloudForZone(zone.Name)
for _, subnet := range cluster.Spec.Subnets {
cloud, known := fi.GuessCloudForZone(subnet.Zone)
if known {
glog.Infof("Inferred --cloud=%s from zone %q", cloud, zone.Name)
glog.Infof("Inferred --cloud=%s from zone %q", cloud, subnet.Zone)
cluster.Spec.CloudProvider = string(cloud)
break
}
Expand All @@ -393,21 +410,25 @@ func RunCreateCluster(f *util.Factory, out io.Writer, c *CreateClusterOptions) e
}
}

//Bastion
if c.Topology != "" {
if c.Topology == api.TopologyPublic && c.Bastion == true {
return fmt.Errorf("Bastion supports --topology='private' only.")
}
// Network Topology
if c.Topology == "" {
// The flag default should have set this, but we might be being called as a library
glog.Infof("Empty topology. Defaulting to public topology")
c.Topology = api.TopologyPublic
}

// Network Topology
switch c.Topology {
case api.TopologyPublic:
cluster.Spec.Topology = &api.TopologySpec{
Masters: api.TopologyPublic,
Nodes: api.TopologyPublic,
Bastion: &api.BastionSpec{Enable: c.Bastion},
//Bastion: &api.BastionSpec{Enable: c.Bastion},
}

if c.Bastion {
return fmt.Errorf("Bastion supports --topology='private' only.")
}

case api.TopologyPrivate:
if !supportsPrivateTopology(cluster.Spec.Networking) {
return fmt.Errorf("Invalid networking option %s. Currently only '--networking cni', '--networking kopeio-vxlan', '--networking weave' are supported for private topologies", c.Networking)
Expand All @@ -416,19 +437,17 @@ func RunCreateCluster(f *util.Factory, out io.Writer, c *CreateClusterOptions) e
Masters: api.TopologyPrivate,
Nodes: api.TopologyPrivate,
}
cluster.Spec.Topology.Bastion = &api.BastionSpec{Enable: c.Bastion}
case "":
glog.Warningf("Empty topology. Defaulting to public topology without bastion")
cluster.Spec.Topology = &api.TopologySpec{
Masters: api.TopologyPublic,
Nodes: api.TopologyPublic,
Bastion: &api.BastionSpec{Enable: false},

if c.Bastion {
bastionGroup := &api.InstanceGroup{}
bastionGroup.Spec.Role = api.InstanceGroupRoleBastion
bastionGroup.ObjectMeta.Name = "bastions"
instanceGroups = append(instanceGroups, bastionGroup)
}

default:
return fmt.Errorf("Invalid topology %s.", c.Topology)
}
cluster.Spec.Topology.Bastion.MachineType = cloudup.DefaultBastionMachineType(cluster)
cluster.Spec.Topology.Bastion.IdleTimeout = cloudup.DefaultBastionIdleTimeout(cluster)

sshPublicKeys := make(map[string][]byte)
if c.SSHPublicKey != "" {
Expand All @@ -443,7 +462,8 @@ func RunCreateCluster(f *util.Factory, out io.Writer, c *CreateClusterOptions) e
}

if c.AdminAccess != "" {
cluster.Spec.AdminAccess = []string{c.AdminAccess}
cluster.Spec.SSHAccess = []string{c.AdminAccess}
cluster.Spec.APIAccess = []string{c.AdminAccess}
}

err = cluster.PerformAssignments()
Expand Down
7 changes: 6 additions & 1 deletion cmd/kops/create_ig.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,12 @@ func RunCreateInstanceGroup(f *util.Factory, cmd *cobra.Command, args []string,
// Populate some defaults
ig := &api.InstanceGroup{}
ig.ObjectMeta.Name = groupName
ig.Spec.Role = api.InstanceGroupRole(options.Role)

role, ok := api.ParseInstanceGroupRole(options.Role, true)
if !ok {
return fmt.Errorf("unknown role %q", options.Role)
}
ig.Spec.Role = role

ig, err = cloudup.PopulateInstanceGroupSpec(cluster, ig, channel)
if err != nil {
Expand Down
12 changes: 6 additions & 6 deletions cmd/kops/get_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,14 +109,14 @@ func (c *GetClustersCmd) Run(args []string) error {
t.AddColumn("CLOUD", func(c *api.Cluster) string {
return c.Spec.CloudProvider
})
t.AddColumn("ZONES", func(c *api.Cluster) string {
var zoneNames []string
for _, z := range c.Spec.Zones {
zoneNames = append(zoneNames, z.Name)
t.AddColumn("SUBNETS", func(c *api.Cluster) string {
var subnetNames []string
for _, s := range c.Spec.Subnets {
subnetNames = append(subnetNames, s.SubnetName)
}
return strings.Join(zoneNames, ",")
return strings.Join(subnetNames, ",")
})
return t.Render(clusters, os.Stdout, "NAME", "CLOUD", "ZONES")
return t.Render(clusters, os.Stdout, "NAME", "CLOUD", "SUBNETS")

case OutputYaml:
for _, cluster := range clusters {
Expand Down
6 changes: 3 additions & 3 deletions cmd/kops/get_instancegroups.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,16 +107,16 @@ func (c *GetInstanceGroupsCmd) Run(args []string) error {
t.AddColumn("MACHINETYPE", func(c *api.InstanceGroup) string {
return c.Spec.MachineType
})
t.AddColumn("ZONES", func(c *api.InstanceGroup) string {
return strings.Join(c.Spec.Zones, ",")
t.AddColumn("SUBNETS", func(c *api.InstanceGroup) string {
return strings.Join(c.Spec.Subnets, ",")
})
t.AddColumn("MIN", func(c *api.InstanceGroup) string {
return intPointerToString(c.Spec.MinSize)
})
t.AddColumn("MAX", func(c *api.InstanceGroup) string {
return intPointerToString(c.Spec.MaxSize)
})
return t.Render(instancegroups, os.Stdout, "NAME", "ROLE", "MACHINETYPE", "MIN", "MAX", "ZONES")
return t.Render(instancegroups, os.Stdout, "NAME", "ROLE", "MACHINETYPE", "MIN", "MAX", "SUBNETS")

case OutputYaml:
for _, ig := range instancegroups {
Expand Down
14 changes: 9 additions & 5 deletions cmd/kops/rollingupdate_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,13 @@ import (
)

type RollingUpdateClusterCmd struct {
Yes bool
Force bool
CloudOnly bool
MasterInterval time.Duration
NodeInterval time.Duration
Yes bool
Force bool
CloudOnly bool

MasterInterval time.Duration
NodeInterval time.Duration
BastionInterval time.Duration

cobraCommand *cobra.Command
}
Expand All @@ -57,8 +59,10 @@ func init() {
cmd.Flags().BoolVar(&rollingupdateCluster.Yes, "yes", false, "perform rolling update without confirmation")
cmd.Flags().BoolVar(&rollingupdateCluster.Force, "force", false, "Force rolling update, even if no changes")
cmd.Flags().BoolVar(&rollingupdateCluster.CloudOnly, "cloudonly", false, "Perform rolling update without confirming progress with k8s")

cmd.Flags().DurationVar(&rollingupdateCluster.MasterInterval, "master-interval", 5*time.Minute, "Time to wait between restarting masters")
cmd.Flags().DurationVar(&rollingupdateCluster.NodeInterval, "node-interval", 2*time.Minute, "Time to wait between restarting nodes")
cmd.Flags().DurationVar(&rollingupdateCluster.BastionInterval, "bastion-interval", 5*time.Minute, "Time to wait between restarting bastions")

cmd.Run = func(cmd *cobra.Command, args []string) {
err := rollingupdateCluster.Run(args)
Expand Down
20 changes: 3 additions & 17 deletions cmd/kops/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ import (
"github.com/spf13/viper"
"k8s.io/kops/cmd/kops/util"
kopsapi "k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/pkg/apis/kops/v1alpha1"
"k8s.io/kops/pkg/client/simple"
"k8s.io/kops/upup/pkg/kutil"
k8sapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
"k8s.io/kubernetes/pkg/util/validation/field"

// Register our APIs
_ "k8s.io/kops/pkg/apis/kops/install"
)

type Factory interface {
Expand Down Expand Up @@ -63,28 +64,13 @@ It allows you to create, destroy, upgrade and maintain clusters.`,
}

func Execute() {
if err := initializeSchemas(); err != nil {
exitWithError(fmt.Errorf("initialization error: %v", err))
}

goflag.Set("logtostderr", "true")
goflag.CommandLine.Parse([]string{})
if err := rootCommand.cobraCommand.Execute(); err != nil {
exitWithError(err)
}
}

func initializeSchemas() error {
scheme := k8sapi.Scheme //runtime.NewScheme()
if err := kopsapi.AddToScheme(scheme); err != nil {
return err
}
if err := v1alpha1.AddToScheme(scheme); err != nil {
return err
}
return nil
}

func init() {
cobra.OnInitialize(initConfig)

Expand Down
12 changes: 6 additions & 6 deletions cmd/kops/toolbox_convert_imported.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,17 +86,17 @@ func (c *ConvertImportedCmd) Run() error {
}

// TODO: Switch to cloudup.BuildCloud
if len(cluster.Spec.Zones) == 0 {
return fmt.Errorf("Configuration must include Zones")
if len(cluster.Spec.Subnets) == 0 {
return fmt.Errorf("Configuration must include Subnets")
}

region := ""
for _, zone := range cluster.Spec.Zones {
if len(zone.Name) <= 2 {
return fmt.Errorf("Invalid AWS zone: %q", zone.Name)
for _, subnet := range cluster.Spec.Subnets {
if len(subnet.SubnetName) <= 2 {
return fmt.Errorf("Invalid AWS zone: %q", subnet.Zone)
}

zoneRegion := zone.Name[:len(zone.Name)-1]
zoneRegion := subnet.Zone[:len(subnet.Zone)-1]
if region != "" && zoneRegion != region {
return fmt.Errorf("Clusters cannot span multiple regions")
}
Expand Down
3 changes: 3 additions & 0 deletions cmd/kops/util/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ import (
"k8s.io/kops/pkg/client/simple/vfsclientset"
"k8s.io/kops/util/pkg/vfs"
"k8s.io/kubernetes/pkg/util/validation/field"

// Register our APIs
_ "k8s.io/kops/pkg/apis/kops/install"
)

type FactoryOptions struct {
Expand Down
Loading

0 comments on commit fed6831

Please sign in to comment.