- updating the aws model to make use of the aws template and mixed instance policies

This commit is contained in:
Rohith 2018-12-28 15:39:37 +00:00
parent 39db0816df
commit 761d760c11
8 changed files with 361 additions and 346 deletions

View File

@ -6,7 +6,6 @@ go_library(
"api_loadbalancer.go", "api_loadbalancer.go",
"autoscalinggroup.go", "autoscalinggroup.go",
"context.go", "context.go",
"convenience.go",
], ],
importpath = "k8s.io/kops/pkg/model/awsmodel", importpath = "k8s.io/kops/pkg/model/awsmodel",
visibility = ["//visibility:public"], visibility = ["//visibility:public"],

View File

@ -21,27 +21,31 @@ import (
"sort" "sort"
"time" "time"
"github.com/golang/glog"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/kops/pkg/apis/kops" "k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/pkg/dns" "k8s.io/kops/pkg/dns"
"k8s.io/kops/pkg/featureflag" "k8s.io/kops/pkg/featureflag"
"k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup/awstasks" "k8s.io/kops/upup/pkg/fi/cloudup/awstasks"
"k8s.io/kops/upup/pkg/fi/fitasks" "k8s.io/kops/upup/pkg/fi/fitasks"
"github.com/golang/glog"
"k8s.io/apimachinery/pkg/util/sets"
) )
// LoadBalancerDefaultIdleTimeout is the default idle time for the ELB
const LoadBalancerDefaultIdleTimeout = 5 * time.Minute const LoadBalancerDefaultIdleTimeout = 5 * time.Minute
// APILoadBalancerBuilder builds a LoadBalancer for accessing the API // APILoadBalancerBuilder builds a LoadBalancer for accessing the API
type APILoadBalancerBuilder struct { type APILoadBalancerBuilder struct {
*AWSModelContext *AWSModelContext
Lifecycle *fi.Lifecycle Lifecycle *fi.Lifecycle
SecurityLifecycle *fi.Lifecycle SecurityLifecycle *fi.Lifecycle
} }
var _ fi.ModelBuilder = &APILoadBalancerBuilder{} var _ fi.ModelBuilder = &APILoadBalancerBuilder{}
// Build is responsible for building the KubeAPI tasks for the aws model
func (b *APILoadBalancerBuilder) Build(c *fi.ModelBuilderContext) error { func (b *APILoadBalancerBuilder) Build(c *fi.ModelBuilderContext) error {
// Configuration where an ELB fronts the API // Configuration where an ELB fronts the API
if !b.UseLoadBalancerForAPI() { if !b.UseLoadBalancerForAPI() {
@ -116,10 +120,10 @@ func (b *APILoadBalancerBuilder) Build(c *fi.ModelBuilderContext) error {
} }
elb = &awstasks.LoadBalancer{ elb = &awstasks.LoadBalancer{
Name: s("api." + b.ClusterName()), Name: fi.String("api." + b.ClusterName()),
Lifecycle: b.Lifecycle, Lifecycle: b.Lifecycle,
LoadBalancerName: s(loadBalancerName), LoadBalancerName: fi.String(loadBalancerName),
SecurityGroups: []*awstasks.SecurityGroup{ SecurityGroups: []*awstasks.SecurityGroup{
b.LinkToELBSecurityGroup("api"), b.LinkToELBSecurityGroup("api"),
}, },
@ -128,21 +132,21 @@ func (b *APILoadBalancerBuilder) Build(c *fi.ModelBuilderContext) error {
// Configure fast-recovery health-checks // Configure fast-recovery health-checks
HealthCheck: &awstasks.LoadBalancerHealthCheck{ HealthCheck: &awstasks.LoadBalancerHealthCheck{
Target: s("SSL:443"), Target: fi.String("SSL:443"),
Timeout: i64(5), Timeout: fi.Int64(5),
Interval: i64(10), Interval: fi.Int64(10),
HealthyThreshold: i64(2), HealthyThreshold: fi.Int64(2),
UnhealthyThreshold: i64(2), UnhealthyThreshold: fi.Int64(2),
}, },
ConnectionSettings: &awstasks.LoadBalancerConnectionSettings{ ConnectionSettings: &awstasks.LoadBalancerConnectionSettings{
IdleTimeout: i64(int64(idleTimeout.Seconds())), IdleTimeout: fi.Int64(int64(idleTimeout.Seconds())),
}, },
} }
switch lbSpec.Type { switch lbSpec.Type {
case kops.LoadBalancerTypeInternal: case kops.LoadBalancerTypeInternal:
elb.Scheme = s("internal") elb.Scheme = fi.String("internal")
case kops.LoadBalancerTypePublic: case kops.LoadBalancerTypePublic:
elb.Scheme = nil elb.Scheme = nil
default: default:
@ -156,12 +160,11 @@ func (b *APILoadBalancerBuilder) Build(c *fi.ModelBuilderContext) error {
var lbSG *awstasks.SecurityGroup var lbSG *awstasks.SecurityGroup
{ {
lbSG = &awstasks.SecurityGroup{ lbSG = &awstasks.SecurityGroup{
Name: s(b.ELBSecurityGroupName("api")), Name: fi.String(b.ELBSecurityGroupName("api")),
Lifecycle: b.SecurityLifecycle, Lifecycle: b.SecurityLifecycle,
Description: fi.String("Security group for api ELB"),
VPC: b.LinkToVPC(),
Description: s("Security group for api ELB"),
RemoveExtraRules: []string{"port=443"}, RemoveExtraRules: []string{"port=443"},
VPC: b.LinkToVPC(),
} }
lbSG.Tags = b.CloudTags(*lbSG.Name, false) lbSG.Tags = b.CloudTags(*lbSG.Name, false)
@ -176,12 +179,11 @@ func (b *APILoadBalancerBuilder) Build(c *fi.ModelBuilderContext) error {
// Allow traffic from ELB to egress freely // Allow traffic from ELB to egress freely
{ {
t := &awstasks.SecurityGroupRule{ t := &awstasks.SecurityGroupRule{
Name: s("api-elb-egress"), Name: fi.String("api-elb-egress"),
Lifecycle: b.SecurityLifecycle, Lifecycle: b.SecurityLifecycle,
CIDR: fi.String("0.0.0.0/0"),
SecurityGroup: lbSG,
Egress: fi.Bool(true), Egress: fi.Bool(true),
CIDR: s("0.0.0.0/0"), SecurityGroup: lbSG,
} }
c.AddTask(t) c.AddTask(t)
} }
@ -189,18 +191,16 @@ func (b *APILoadBalancerBuilder) Build(c *fi.ModelBuilderContext) error {
// Allow traffic into the ELB from KubernetesAPIAccess CIDRs // Allow traffic into the ELB from KubernetesAPIAccess CIDRs
{ {
for _, cidr := range b.Cluster.Spec.KubernetesAPIAccess { for _, cidr := range b.Cluster.Spec.KubernetesAPIAccess {
t := &awstasks.SecurityGroupRule{
// Allow https traffic Name: fi.String("https-api-elb-" + cidr),
c.AddTask(&awstasks.SecurityGroupRule{ Lifecycle: b.SecurityLifecycle,
Name: s("https-api-elb-" + cidr), CIDR: fi.String(cidr),
Lifecycle: b.SecurityLifecycle, FromPort: fi.Int64(443),
Protocol: fi.String("tcp"),
SecurityGroup: lbSG, SecurityGroup: lbSG,
CIDR: s(cidr), ToPort: fi.Int64(443),
FromPort: i64(443), }
ToPort: i64(443), c.AddTask(t)
Protocol: s("tcp"),
})
// Allow ICMP traffic required for PMTU discovery // Allow ICMP traffic required for PMTU discovery
c.AddTask(&awstasks.SecurityGroupRule{ c.AddTask(&awstasks.SecurityGroupRule{
@ -220,11 +220,10 @@ func (b *APILoadBalancerBuilder) Build(c *fi.ModelBuilderContext) error {
{ {
for _, id := range b.Cluster.Spec.API.LoadBalancer.AdditionalSecurityGroups { for _, id := range b.Cluster.Spec.API.LoadBalancer.AdditionalSecurityGroups {
t := &awstasks.SecurityGroup{ t := &awstasks.SecurityGroup{
Name: fi.String(id), Name: fi.String(id),
ID: fi.String(id),
Shared: fi.Bool(true),
Lifecycle: b.SecurityLifecycle, Lifecycle: b.SecurityLifecycle,
ID: fi.String(id),
Shared: fi.Bool(true),
} }
if err := c.EnsureTask(t); err != nil { if err := c.EnsureTask(t); err != nil {
return err return err
@ -242,17 +241,15 @@ func (b *APILoadBalancerBuilder) Build(c *fi.ModelBuilderContext) error {
{ {
for _, masterGroup := range masterGroups { for _, masterGroup := range masterGroups {
suffix := masterGroup.Suffix suffix := masterGroup.Suffix
t := &awstasks.SecurityGroupRule{ c.AddTask(&awstasks.SecurityGroupRule{
Name: s(fmt.Sprintf("https-elb-to-master%s", suffix)), Name: fi.String(fmt.Sprintf("https-elb-to-master%s", suffix)),
Lifecycle: b.SecurityLifecycle, Lifecycle: b.SecurityLifecycle,
FromPort: fi.Int64(443),
Protocol: fi.String("tcp"),
SecurityGroup: masterGroup.Task, SecurityGroup: masterGroup.Task,
SourceGroup: lbSG, SourceGroup: lbSG,
FromPort: i64(443), ToPort: fi.Int64(443),
ToPort: i64(443), })
Protocol: s("tcp"),
}
c.AddTask(t)
} }
} }
@ -273,15 +270,12 @@ func (b *APILoadBalancerBuilder) Build(c *fi.ModelBuilderContext) error {
// is already done as part of the Elastigroup's creation, if needed. // is already done as part of the Elastigroup's creation, if needed.
if !featureflag.Spotinst.Enabled() { if !featureflag.Spotinst.Enabled() {
for _, ig := range b.MasterInstanceGroups() { for _, ig := range b.MasterInstanceGroups() {
t := &awstasks.LoadBalancerAttachment{ c.AddTask(&awstasks.LoadBalancerAttachment{
Name: s("api-" + ig.ObjectMeta.Name), Name: fi.String("api-" + ig.ObjectMeta.Name),
Lifecycle: b.Lifecycle, Lifecycle: b.Lifecycle,
LoadBalancer: b.LinkToELB("api"),
AutoscalingGroup: b.LinkToAutoscalingGroup(ig), AutoscalingGroup: b.LinkToAutoscalingGroup(ig),
} LoadBalancer: b.LinkToELB("api"),
})
c.AddTask(t)
} }
} }

View File

@ -50,256 +50,281 @@ var _ fi.ModelBuilder = &AutoscalingGroupModelBuilder{}
// Build is responsible for constructing the aws autoscaling group from the kops spec // Build is responsible for constructing the aws autoscaling group from the kops spec
func (b *AutoscalingGroupModelBuilder) Build(c *fi.ModelBuilderContext) error { func (b *AutoscalingGroupModelBuilder) Build(c *fi.ModelBuilderContext) error {
var err error
for _, ig := range b.InstanceGroups { for _, ig := range b.InstanceGroups {
name := b.AutoscalingGroupName(ig) name := b.AutoscalingGroupName(ig)
// LaunchConfiguration // @check if his instancegroup is backed by a fleet and overide with a launch template
var launchConfiguration *awstasks.LaunchConfiguration task, err := func() (fi.Task, error) {
{ switch UseLaunchTemplate(ig) {
volumeSize := fi.Int32Value(ig.Spec.RootVolumeSize) case true:
if volumeSize == 0 { return b.buildLaunchTemplateTask(c, name, ig)
volumeSize, err = defaults.DefaultInstanceGroupVolumeSize(ig.Spec.Role) default:
if err != nil { return b.buildLaunchConfigurationTask(c, name, ig)
return err
}
} }
}()
if err != nil {
return err
}
c.AddTask(task)
volumeType := fi.StringValue(ig.Spec.RootVolumeType) // @step: now lets build the autoscaling group task
if volumeType == "" { tsk, err := b.buildAutoScalingGroupTask(c, name, ig)
volumeType = DefaultVolumeType if err != nil {
} return err
}
volumeIops := fi.Int32Value(ig.Spec.RootVolumeIops) switch UseLaunchTemplate(ig) {
if volumeIops <= 0 { case true:
volumeIops = DefaultVolumeIops tsk.LaunchTemplate = task.(*awstasks.LaunchTemplate)
} default:
tsk.LaunchConfiguration = task.(*awstasks.LaunchConfiguration)
link, err := b.LinkToIAMInstanceProfile(ig)
if err != nil {
return fmt.Errorf("unable to find IAM profile link for instance group %q: %v", ig.ObjectMeta.Name, err)
}
var sgLink *awstasks.SecurityGroup
if ig.Spec.SecurityGroupOverride != nil {
glog.V(1).Infof("WARNING: You are overwriting the Instance Groups, Security Group. When this is done you are responsible for ensure the correct rules!")
sgName := fmt.Sprintf("%v-%v", fi.StringValue(ig.Spec.SecurityGroupOverride), ig.Spec.Role)
sgLink = &awstasks.SecurityGroup{
Name: &sgName,
ID: ig.Spec.SecurityGroupOverride,
Shared: fi.Bool(true),
}
} else {
sgLink = b.LinkToSecurityGroup(ig.Spec.Role)
}
t := &awstasks.LaunchConfiguration{
Name: s(name),
Lifecycle: b.Lifecycle,
IAMInstanceProfile: link,
ImageID: s(ig.Spec.Image),
InstanceMonitoring: ig.Spec.DetailedInstanceMonitoring,
InstanceType: s(strings.Split(ig.Spec.MachineType, ",")[0]),
RootVolumeOptimization: ig.Spec.RootVolumeOptimization,
RootVolumeSize: i64(int64(volumeSize)),
RootVolumeType: s(volumeType),
SecurityGroups: []*awstasks.SecurityGroup{sgLink},
}
if volumeType == ec2.VolumeTypeIo1 {
t.RootVolumeIops = i64(int64(volumeIops))
}
if ig.Spec.Tenancy != "" {
t.Tenancy = s(ig.Spec.Tenancy)
}
// @step: add any additional security groups to the instance
for _, id := range ig.Spec.AdditionalSecurityGroups {
sgTask := &awstasks.SecurityGroup{
Name: fi.String(id),
ID: fi.String(id),
Lifecycle: b.SecurityLifecycle,
Shared: fi.Bool(true),
}
if err := c.EnsureTask(sgTask); err != nil {
return err
}
t.SecurityGroups = append(t.SecurityGroups, sgTask)
}
// @step: add any additional block devices to the launch configuration
for _, x := range ig.Spec.Volumes {
if x.Type == "" {
x.Type = DefaultVolumeType
}
if x.Type == ec2.VolumeTypeIo1 {
if x.Iops == nil {
x.Iops = fi.Int64(DefaultVolumeIops)
}
} else {
x.Iops = nil
}
t.BlockDeviceMappings = append(t.BlockDeviceMappings, &awstasks.BlockDeviceMapping{
DeviceName: fi.String(x.Device),
EbsDeleteOnTermination: fi.Bool(true),
EbsEncrypted: x.Encrypted,
EbsVolumeIops: x.Iops,
EbsVolumeSize: fi.Int64(x.Size),
EbsVolumeType: fi.String(x.Type),
})
}
if t.SSHKey, err = b.LinkToSSHKey(); err != nil {
return err
}
if t.UserData, err = b.BootstrapScript.ResourceNodeUp(ig, b.Cluster); err != nil {
return err
}
if fi.StringValue(ig.Spec.MaxPrice) != "" {
spotPrice := fi.StringValue(ig.Spec.MaxPrice)
t.SpotPrice = spotPrice
}
{
// TODO: Wrapper / helper class to analyze clusters
subnetMap := make(map[string]*kops.ClusterSubnetSpec)
for i := range b.Cluster.Spec.Subnets {
subnet := &b.Cluster.Spec.Subnets[i]
subnetMap[subnet.Name] = subnet
}
var subnetType kops.SubnetType
for _, subnetName := range ig.Spec.Subnets {
subnet := subnetMap[subnetName]
if subnet == nil {
return fmt.Errorf("InstanceGroup %q uses subnet %q that does not exist", ig.ObjectMeta.Name, subnetName)
}
if subnetType != "" && subnetType != subnet.Type {
return fmt.Errorf("InstanceGroup %q cannot be in subnets of different Type", ig.ObjectMeta.Name)
}
subnetType = subnet.Type
}
associatePublicIP := true
switch subnetType {
case kops.SubnetTypePublic, kops.SubnetTypeUtility:
associatePublicIP = true
if ig.Spec.AssociatePublicIP != nil {
associatePublicIP = *ig.Spec.AssociatePublicIP
}
case kops.SubnetTypePrivate:
associatePublicIP = false
if ig.Spec.AssociatePublicIP != nil {
// This isn't meaningful - private subnets can't have public ip
//associatePublicIP = *ig.Spec.AssociatePublicIP
if *ig.Spec.AssociatePublicIP {
glog.Warningf("Ignoring AssociatePublicIP=true for private InstanceGroup %q", ig.ObjectMeta.Name)
}
}
default:
return fmt.Errorf("unknown subnet type %q", subnetType)
}
t.AssociatePublicIP = &associatePublicIP
}
c.AddTask(t)
launchConfiguration = t
} }
// AutoscalingGroup c.AddTask(tsk)
{
t := &awstasks.AutoscalingGroup{
Name: s(name),
Lifecycle: b.Lifecycle,
Granularity: s("1Minute"), // @step: add any external load balancer attachments
Metrics: []string{ if err := b.buildExternalLoadBalancerTasks(c, ig); err != nil {
"GroupDesiredCapacity", return err
"GroupInServiceInstances", }
"GroupMaxSize", }
"GroupMinSize",
"GroupPendingInstances", return nil
"GroupStandbyInstances", }
"GroupTerminatingInstances",
"GroupTotalInstances", // buildLaunchTemplateTask is responsible for creating the template task into the aws model
}, func (b *AutoscalingGroupModelBuilder) buildLaunchTemplateTask(c *fi.ModelBuilderContext, name string, ig *kops.InstanceGroup) (*awstasks.LaunchTemplate, error) {
LaunchConfiguration: launchConfiguration, lc, err := b.buildLaunchConfigurationTask(c, name, ig)
} if err != nil {
return nil, err
minSize := int32(1) }
maxSize := int32(1)
if ig.Spec.MinSize != nil { // @TODO check if there any a better way of doing this .. initially I had a type LaunchTemplate which included
minSize = fi.Int32Value(ig.Spec.MinSize) // LaunchConfiguration as an anonymous field, bit given up the task dependency walker works this caused issues, due
} else if ig.Spec.Role == kops.InstanceGroupRoleNode { // to the creation of a implict dependency
minSize = 2 return &awstasks.LaunchTemplate{
} Name: fi.String(name),
if ig.Spec.MaxSize != nil { Lifecycle: b.Lifecycle,
maxSize = *ig.Spec.MaxSize AssociatePublicIP: lc.AssociatePublicIP,
} else if ig.Spec.Role == kops.InstanceGroupRoleNode { IAMInstanceProfile: lc.IAMInstanceProfile,
maxSize = 2 ImageID: lc.ImageID,
} InstanceMonitoring: lc.InstanceMonitoring,
InstanceType: lc.InstanceType,
t.MinSize = i64(int64(minSize)) RootVolumeOptimization: lc.RootVolumeOptimization,
t.MaxSize = i64(int64(maxSize)) RootVolumeSize: lc.RootVolumeSize,
RootVolumeIops: lc.RootVolumeIops,
subnets, err := b.GatherSubnets(ig) RootVolumeType: lc.RootVolumeType,
if err != nil { SSHKey: lc.SSHKey,
return err SecurityGroups: lc.SecurityGroups,
} SpotPrice: lc.SpotPrice,
if len(subnets) == 0 { Tenancy: lc.Tenancy,
return fmt.Errorf("could not determine any subnets for InstanceGroup %q; subnets was %s", ig.ObjectMeta.Name, ig.Spec.Subnets) UserData: lc.UserData,
} }, nil
for _, subnet := range subnets { }
t.Subnets = append(t.Subnets, b.LinkToSubnet(subnet))
} // buildLaunchConfigurationTask is responsible for building a launch configuration task into the model
func (b *AutoscalingGroupModelBuilder) buildLaunchConfigurationTask(c *fi.ModelBuilderContext, name string, ig *kops.InstanceGroup) (*awstasks.LaunchConfiguration, error) {
tags, err := b.CloudTagsForInstanceGroup(ig)
if err != nil { // @step: add the volume type, size and spec
return fmt.Errorf("error building cloud tags: %v", err) volumeType := &DefaultVolumeType
} var volumeIops *int64
t.Tags = tags
size, err := defaults.DefaultInstanceGroupVolumeSize(ig.Spec.Role)
processes := []string{} if err != nil {
for _, p := range ig.Spec.SuspendProcesses { return nil, err
processes = append(processes, p) }
} volumeSize := fi.Int64(int64(size))
t.SuspendProcesses = &processes
if ig.Spec.RootVolumeSize != nil {
c.AddTask(t) volumeSize = fi.Int64(int64(fi.Int32Value(ig.Spec.RootVolumeSize)))
} }
if ig.Spec.RootVolumeType != nil {
// External Load Balancer/TargetGroup Attachments volumeType = ig.Spec.RootVolumeType
{ }
for _, lb := range ig.Spec.ExternalLoadBalancers { if fi.StringValue(volumeType) == ec2.VolumeTypeIo1 {
if lb.LoadBalancerName != nil { volumeIops = DefaultVolumeIops
t := &awstasks.ExternalLoadBalancerAttachment{ if ig.Spec.RootVolumeIops != nil {
Name: s("extlb-" + *lb.LoadBalancerName + "-" + ig.Name), volumeIops = fi.Int64(int64(fi.Int32Value(ig.Spec.RootVolumeIops)))
Lifecycle: b.Lifecycle, }
LoadBalancerName: *lb.LoadBalancerName, }
AutoscalingGroup: b.LinkToAutoscalingGroup(ig),
} // @step: if required we add the override for the security group for this instancegroup
sgLink := b.LinkToSecurityGroup(ig.Spec.Role)
c.AddTask(t) if ig.Spec.SecurityGroupOverride != nil {
} sgName := fmt.Sprintf("%v-%v", fi.StringValue(ig.Spec.SecurityGroupOverride), ig.Spec.Role)
sgLink = &awstasks.SecurityGroup{
if lb.TargetGroupARN != nil { ID: ig.Spec.SecurityGroupOverride,
t := &awstasks.ExternalTargetGroupAttachment{ Name: &sgName,
Name: s("exttg-" + *lb.TargetGroupARN + "-" + ig.Name), Shared: fi.Bool(true),
Lifecycle: b.Lifecycle, }
TargetGroupARN: *lb.TargetGroupARN, }
AutoscalingGroup: b.LinkToAutoscalingGroup(ig),
} // @step: add the iam instance profile
link, err := b.LinkToIAMInstanceProfile(ig)
c.AddTask(t) if err != nil {
} return nil, fmt.Errorf("unable to find iam profile link for instance group %q: %v", ig.ObjectMeta.Name, err)
} }
t := &awstasks.LaunchConfiguration{
Name: fi.String(name),
Lifecycle: b.Lifecycle,
IAMInstanceProfile: link,
ImageID: fi.String(ig.Spec.Image),
InstanceMonitoring: ig.Spec.DetailedInstanceMonitoring,
InstanceType: fi.String(strings.Split(ig.Spec.MachineType, ",")[0]),
RootVolumeOptimization: ig.Spec.RootVolumeOptimization,
RootVolumeSize: volumeSize,
RootVolumeIops: volumeIops,
RootVolumeType: volumeType,
SecurityGroups: []*awstasks.SecurityGroup{sgLink},
}
if ig.Spec.Tenancy != "" {
t.Tenancy = fi.String(ig.Spec.Tenancy)
}
// @step: add any additional security groups to the instancegroup
for _, id := range ig.Spec.AdditionalSecurityGroups {
sgTask := &awstasks.SecurityGroup{
ID: fi.String(id),
Lifecycle: b.SecurityLifecycle,
Name: fi.String(id),
Shared: fi.Bool(true),
}
if err := c.EnsureTask(sgTask); err != nil {
return nil, err
}
t.SecurityGroups = append(t.SecurityGroups, sgTask)
}
// @step: attach the ssh key to the instancegroup
if t.SSHKey, err = b.LinkToSSHKey(); err != nil {
return nil, err
}
// @step: add the instancegroup userdata
if t.UserData, err = b.BootstrapScript.ResourceNodeUp(ig, b.Cluster); err != nil {
return nil, err
}
// @step: set up instnce spot pricing
if fi.StringValue(ig.Spec.MaxPrice) != "" {
spotPrice := fi.StringValue(ig.Spec.MaxPrice)
t.SpotPrice = spotPrice
}
// @step: check the subnets are ok and pull together an array for us
subnets, err := b.GatherSubnets(ig)
if err != nil {
return nil, err
}
// @step: check if we can add an public ip to this subnet
switch subnets[0].Type {
case kops.SubnetTypePublic, kops.SubnetTypeUtility:
t.AssociatePublicIP = ig.Spec.AssociatePublicIP
case kops.SubnetTypePrivate:
if ig.Spec.AssociatePublicIP != nil {
if fi.BoolValue(ig.Spec.AssociatePublicIP) {
glog.Warningf("Ignoring AssociatePublicIP=true for private InstanceGroup %q", ig.ObjectMeta.Name)
}
}
}
return t, nil
}
// buildAutoscalingGroupTask is responsible for building the autoscaling task into the model
func (b *AutoscalingGroupModelBuilder) buildAutoScalingGroupTask(c *fi.ModelBuilderContext, name string, ig *kops.InstanceGroup) (*awstasks.AutoscalingGroup, error) {
t := &awstasks.AutoscalingGroup{
Name: fi.String(name),
Lifecycle: b.Lifecycle,
Granularity: fi.String("1Minute"),
Metrics: []string{
"GroupDesiredCapacity",
"GroupInServiceInstances",
"GroupMaxSize",
"GroupMinSize",
"GroupPendingInstances",
"GroupStandbyInstances",
"GroupTerminatingInstances",
"GroupTotalInstances",
},
}
minSize := int32(1)
maxSize := int32(1)
if ig.Spec.MinSize != nil {
minSize = fi.Int32Value(ig.Spec.MinSize)
} else if ig.Spec.Role == kops.InstanceGroupRoleNode {
minSize = 2
}
if ig.Spec.MaxSize != nil {
maxSize = *ig.Spec.MaxSize
} else if ig.Spec.Role == kops.InstanceGroupRoleNode {
maxSize = 2
}
t.MinSize = fi.Int64(int64(minSize))
t.MaxSize = fi.Int64(int64(maxSize))
subnets, err := b.GatherSubnets(ig)
if err != nil {
return nil, err
}
if len(subnets) == 0 {
return nil, fmt.Errorf("could not determine any subnets for InstanceGroup %q; subnets was %s", ig.ObjectMeta.Name, ig.Spec.Subnets)
}
for _, subnet := range subnets {
t.Subnets = append(t.Subnets, b.LinkToSubnet(subnet))
}
tags, err := b.CloudTagsForInstanceGroup(ig)
if err != nil {
return nil, fmt.Errorf("error building cloud tags: %v", err)
}
t.Tags = tags
processes := []string{}
for _, p := range ig.Spec.SuspendProcesses {
processes = append(processes, p)
}
t.SuspendProcesses = &processes
// @step: are we using a mixed instance policy
if ig.Spec.MixedInstancesPolicy != nil {
spec := ig.Spec.MixedInstancesPolicy
t.MixedInstanceOverrides = spec.Instances
t.MixedOnDemandAboveBase = spec.OnDemandAboveBase
t.MixedOnDemandAllocationStrategy = spec.OnDemandAllocationStrategy
t.MixedOnDemandBase = spec.OnDemandBase
t.MixedSpotAllocationStrategy = spec.SpotAllocationStrategy
t.MixedSpotInstancePools = spec.SpotInstancePools
}
return t, nil
}
// buildExternlLoadBalancerTasks is responsible for adding any ELB attachment tasks to the model
func (b *AutoscalingGroupModelBuilder) buildExternalLoadBalancerTasks(c *fi.ModelBuilderContext, ig *kops.InstanceGroup) error {
for _, x := range ig.Spec.ExternalLoadBalancers {
if x.LoadBalancerName != nil {
c.AddTask(&awstasks.ExternalLoadBalancerAttachment{
Name: fi.String("extlb-" + *x.LoadBalancerName + "-" + ig.Name),
Lifecycle: b.Lifecycle,
LoadBalancerName: *x.LoadBalancerName,
AutoscalingGroup: b.LinkToAutoscalingGroup(ig),
})
}
if x.TargetGroupARN != nil {
c.AddTask(&awstasks.ExternalTargetGroupAttachment{
Name: fi.String("exttg-" + *x.TargetGroupARN + "-" + ig.Name),
Lifecycle: b.Lifecycle,
TargetGroupARN: *x.TargetGroupARN,
AutoscalingGroup: b.LinkToAutoscalingGroup(ig),
})
} }
} }

View File

@ -16,8 +16,17 @@ limitations under the License.
package awsmodel package awsmodel
import "k8s.io/kops/pkg/model" import (
"k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/pkg/model"
)
// AWSModelContext provides the context for the aws model
type AWSModelContext struct { type AWSModelContext struct {
*model.KopsModelContext *model.KopsModelContext
} }
// UseLaunchTemplate checks if we need to use a launch template rather than configuration
func UseLaunchTemplate(ig *kops.InstanceGroup) bool {
return ig.Spec.MixedInstancesPolicy != nil
}

View File

@ -1,34 +0,0 @@
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package awsmodel
import "k8s.io/kops/upup/pkg/fi"
// s is a helper that builds a *string from a string value
func s(v string) *string {
return fi.String(v)
}
// i64 is a helper that builds a *int64 from an int64 value
func i64(v int64) *int64 {
return fi.Int64(v)
}
// i32 is a helper that builds a *int32 from an int32 value
func i32(v int32) *int32 {
return fi.Int32(v)
}

View File

@ -96,6 +96,7 @@ func (m *KopsModelContext) GetELBName32(prefix string) string {
return s return s
} }
// ClusterName returns the cluster name
func (m *KopsModelContext) ClusterName() string { func (m *KopsModelContext) ClusterName() string {
return m.Cluster.ObjectMeta.Name return m.Cluster.ObjectMeta.Name
} }
@ -103,6 +104,8 @@ func (m *KopsModelContext) ClusterName() string {
// GatherSubnets maps the subnet names in an InstanceGroup to the ClusterSubnetSpec objects (which are stored on the Cluster) // GatherSubnets maps the subnet names in an InstanceGroup to the ClusterSubnetSpec objects (which are stored on the Cluster)
func (m *KopsModelContext) GatherSubnets(ig *kops.InstanceGroup) ([]*kops.ClusterSubnetSpec, error) { func (m *KopsModelContext) GatherSubnets(ig *kops.InstanceGroup) ([]*kops.ClusterSubnetSpec, error) {
var subnets []*kops.ClusterSubnetSpec var subnets []*kops.ClusterSubnetSpec
var subnetType kops.SubnetType
for _, subnetName := range ig.Spec.Subnets { for _, subnetName := range ig.Spec.Subnets {
var matches []*kops.ClusterSubnetSpec var matches []*kops.ClusterSubnetSpec
for i := range m.Cluster.Spec.Subnets { for i := range m.Cluster.Spec.Subnets {
@ -118,7 +121,18 @@ func (m *KopsModelContext) GatherSubnets(ig *kops.InstanceGroup) ([]*kops.Cluste
return nil, fmt.Errorf("found multiple subnets with name: %q", subnetName) return nil, fmt.Errorf("found multiple subnets with name: %q", subnetName)
} }
subnets = append(subnets, matches[0]) subnets = append(subnets, matches[0])
// @step: check the instance is not cross subnet types
switch subnetType {
case "":
subnetType = matches[0].Type
default:
if matches[0].Type != subnetType {
return nil, fmt.Errorf("found subnets of different types: %v", strings.Join([]string{string(subnetType), string(matches[0].Type)}, ","))
}
}
} }
return subnets, nil return subnets, nil
} }
@ -285,7 +299,7 @@ func (m *KopsModelContext) UseLoadBalancerForAPI() bool {
return m.Cluster.Spec.API.LoadBalancer != nil return m.Cluster.Spec.API.LoadBalancer != nil
} }
// If true then we will use the created loadbalancer for internal kubelet // UseLoadBalancerForInternalAPI check if true then we will use the created loadbalancer for internal kubelet
// connections. The intention here is to make connections to apiserver more // connections. The intention here is to make connections to apiserver more
// HA - see https://github.com/kubernetes/kops/issues/4252 // HA - see https://github.com/kubernetes/kops/issues/4252
func (m *KopsModelContext) UseLoadBalancerForInternalAPI() bool { func (m *KopsModelContext) UseLoadBalancerForInternalAPI() bool {
@ -313,8 +327,8 @@ func (m *KopsModelContext) UsePrivateDNS() bool {
} }
// UseEtcdTLS checks to see if etcd tls is enabled // UseEtcdTLS checks to see if etcd tls is enabled
func (c *KopsModelContext) UseEtcdTLS() bool { func (m *KopsModelContext) UseEtcdTLS() bool {
for _, x := range c.Cluster.Spec.EtcdClusters { for _, x := range m.Cluster.Spec.EtcdClusters {
if x.EnableEtcdTLS { if x.EnableEtcdTLS {
return true return true
} }
@ -324,10 +338,10 @@ func (c *KopsModelContext) UseEtcdTLS() bool {
} }
// KubernetesVersion parses the semver version of kubernetes, from the cluster spec // KubernetesVersion parses the semver version of kubernetes, from the cluster spec
func (c *KopsModelContext) KubernetesVersion() semver.Version { func (m *KopsModelContext) KubernetesVersion() semver.Version {
// TODO: Remove copy-pasting c.f. https://github.com/kubernetes/kops/blob/master/pkg/model/components/context.go#L32 // TODO: Remove copy-pasting c.f. https://github.com/kubernetes/kops/blob/master/pkg/model/components/context.go#L32
kubernetesVersion := c.Cluster.Spec.KubernetesVersion kubernetesVersion := m.Cluster.Spec.KubernetesVersion
if kubernetesVersion == "" { if kubernetesVersion == "" {
glog.Fatalf("KubernetesVersion is required") glog.Fatalf("KubernetesVersion is required")
@ -342,20 +356,21 @@ func (c *KopsModelContext) KubernetesVersion() semver.Version {
} }
// IsKubernetesGTE checks if the kubernetes version is at least version, ignoring prereleases / patches // IsKubernetesGTE checks if the kubernetes version is at least version, ignoring prereleases / patches
func (c *KopsModelContext) IsKubernetesGTE(version string) bool { func (m *KopsModelContext) IsKubernetesGTE(version string) bool {
return util.IsKubernetesGTE(version, c.KubernetesVersion()) return util.IsKubernetesGTE(version, m.KubernetesVersion())
} }
func (c *KopsModelContext) WellKnownServiceIP(id int) (net.IP, error) { // WellKnownServiceIP returns a service ip with the service cidr
return components.WellKnownServiceIP(&c.Cluster.Spec, id) func (m *KopsModelContext) WellKnownServiceIP(id int) (net.IP, error) {
return components.WellKnownServiceIP(&m.Cluster.Spec, id)
} }
// NodePortRange returns the range of ports allocated to NodePorts // NodePortRange returns the range of ports allocated to NodePorts
func (c *KopsModelContext) NodePortRange() (utilnet.PortRange, error) { func (m *KopsModelContext) NodePortRange() (utilnet.PortRange, error) {
// defaultServiceNodePortRange is the default port range for NodePort services. // defaultServiceNodePortRange is the default port range for NodePort services.
defaultServiceNodePortRange := utilnet.PortRange{Base: 30000, Size: 2768} defaultServiceNodePortRange := utilnet.PortRange{Base: 30000, Size: 2768}
kubeApiServer := c.Cluster.Spec.KubeAPIServer kubeApiServer := m.Cluster.Spec.KubeAPIServer
if kubeApiServer != nil && kubeApiServer.ServiceNodePortRange != "" { if kubeApiServer != nil && kubeApiServer.ServiceNodePortRange != "" {
err := defaultServiceNodePortRange.Set(kubeApiServer.ServiceNodePortRange) err := defaultServiceNodePortRange.Set(kubeApiServer.ServiceNodePortRange)
if err != nil { if err != nil {

View File

@ -23,9 +23,12 @@ import (
) )
const ( const (
DefaultVolumeSizeNode = 128 // DefaultVolumeSizeBastion is the default root disk size of a bastion
DefaultVolumeSizeMaster = 64
DefaultVolumeSizeBastion = 32 DefaultVolumeSizeBastion = 32
// DefaultVolumeSizeMaster is the default root disk size of a master
DefaultVolumeSizeMaster = 64
// DefaultVolumeSizeNode is the default root disk size of a node
DefaultVolumeSizeNode = 128
) )
// DefaultInstanceGroupVolumeSize returns the default volume size for nodes in an InstanceGroup with the specified role // DefaultInstanceGroupVolumeSize returns the default volume size for nodes in an InstanceGroup with the specified role

View File

@ -20,14 +20,15 @@ import (
"fmt" "fmt"
"regexp" "regexp"
"github.com/golang/glog"
"k8s.io/kops/pkg/apis/kops" "k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/pkg/pki" "k8s.io/kops/pkg/pki"
"k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup/awstasks" "k8s.io/kops/upup/pkg/fi/cloudup/awstasks"
"github.com/golang/glog"
) )
// SecurityGroupName returns the security group name for the specific role
func (b *KopsModelContext) SecurityGroupName(role kops.InstanceGroupRole) string { func (b *KopsModelContext) SecurityGroupName(role kops.InstanceGroupRole) string {
switch role { switch role {
case kops.InstanceGroupRoleBastion: case kops.InstanceGroupRoleBastion:
@ -42,11 +43,13 @@ func (b *KopsModelContext) SecurityGroupName(role kops.InstanceGroupRole) string
} }
} }
// LinkToSecurityGroup creates a task link the security group to the instncegroup
func (b *KopsModelContext) LinkToSecurityGroup(role kops.InstanceGroupRole) *awstasks.SecurityGroup { func (b *KopsModelContext) LinkToSecurityGroup(role kops.InstanceGroupRole) *awstasks.SecurityGroup {
name := b.SecurityGroupName(role) name := b.SecurityGroupName(role)
return &awstasks.SecurityGroup{Name: &name} return &awstasks.SecurityGroup{Name: &name}
} }
// AutoscalingGroupName derives the autoscaling group name for us
func (b *KopsModelContext) AutoscalingGroupName(ig *kops.InstanceGroup) string { func (b *KopsModelContext) AutoscalingGroupName(ig *kops.InstanceGroup) string {
switch ig.Spec.Role { switch ig.Spec.Role {
case kops.InstanceGroupRoleMaster: case kops.InstanceGroupRoleMaster:
@ -77,6 +80,7 @@ func (b *KopsModelContext) LinkToELBSecurityGroup(prefix string) *awstasks.Secur
return &awstasks.SecurityGroup{Name: &name} return &awstasks.SecurityGroup{Name: &name}
} }
// ELBName returns ELB name plus cluster name
func (b *KopsModelContext) ELBName(prefix string) string { func (b *KopsModelContext) ELBName(prefix string) string {
return prefix + "." + b.ClusterName() return prefix + "." + b.ClusterName()
} }