diff --git a/pkg/model/master_volumes.go b/pkg/model/master_volumes.go index cebd12d8c1c6d..b25d93778b9ab 100644 --- a/pkg/model/master_volumes.go +++ b/pkg/model/master_volumes.go @@ -30,6 +30,8 @@ import ( "k8s.io/kops/upup/pkg/fi/cloudup/dotasks" "k8s.io/kops/upup/pkg/fi/cloudup/gce" "k8s.io/kops/upup/pkg/fi/cloudup/gcetasks" + "k8s.io/kops/upup/pkg/fi/cloudup/openstack" + "k8s.io/kops/upup/pkg/fi/cloudup/openstacktasks" ) const ( @@ -95,6 +97,11 @@ func (b *MasterVolumeBuilder) Build(c *fi.ModelBuilderContext) error { b.addVSphereVolume(c, name, volumeSize, zone, etcd, m, allMembers) case kops.CloudProviderBareMetal: glog.Fatalf("BareMetal not implemented") + case kops.CloudProviderOpenstack: + err = b.addOpenstackVolume(c, name, volumeSize, zone, etcd, m, allMembers) + if err != nil { + return err + } default: return fmt.Errorf("unknown cloudprovider %q", b.Cluster.Spec.CloudProvider) } @@ -205,3 +212,32 @@ func (b *MasterVolumeBuilder) addGCEVolume(c *fi.ModelBuilderContext, name strin func (b *MasterVolumeBuilder) addVSphereVolume(c *fi.ModelBuilderContext, name string, volumeSize int32, zone string, etcd *kops.EtcdClusterSpec, m *kops.EtcdMemberSpec, allMembers []string) { fmt.Print("addVSphereVolume to be implemented") } + +func (b *MasterVolumeBuilder) addOpenstackVolume(c *fi.ModelBuilderContext, name string, volumeSize int32, zone string, etcd *kops.EtcdClusterSpec, m *kops.EtcdMemberSpec, allMembers []string) error { + volumeType := fi.StringValue(m.VolumeType) + if volumeType == "" { + return fmt.Errorf("must set ETCDMemberSpec.VolumeType on Openstack platform") + } + + // The tags are how protokube knows to mount the volume and use it for etcd + tags := make(map[string]string) + // Apply all user defined labels on the volumes + for k, v := range b.Cluster.Spec.CloudLabels { + tags[k] = v + } + // This is the configuration of the etcd cluster + tags[openstack.TagNameEtcdClusterPrefix+etcd.Name] = m.Name + "/" + strings.Join(allMembers, ",") + // This says "only mount on a master" + tags[openstack.TagNameRolePrefix+"master"] = "1" + + t := &openstacktasks.Volume{ + Name: s(name), + AvailabilityZone: s(zone), + VolumeType: s(volumeType), + SizeGB: fi.Int64(int64(volumeSize)), + Tags: tags, + Lifecycle: b.Lifecycle, + } + + c.AddTask(t) +} diff --git a/upup/pkg/fi/cloudup/openstack/cloud.go b/upup/pkg/fi/cloudup/openstack/cloud.go index 10ebd3715093e..bee8abf85609d 100644 --- a/upup/pkg/fi/cloudup/openstack/cloud.go +++ b/upup/pkg/fi/cloudup/openstack/cloud.go @@ -33,6 +33,10 @@ import ( "k8s.io/kubernetes/federation/pkg/dnsprovider" ) +const TagNameEtcdClusterPrefix = "k8s.io/etcd/" +const TagNameRolePrefix = "k8s.io/role/" +const TagClusterName = "KubernetesCluster" + // readBackoff is the backoff strategy for openstack read retries. var readBackoff = wait.Backoff{ Duration: time.Second, @@ -165,7 +169,7 @@ func (c *openstackCloud) ListVolumes(opt cinder.ListOpts) ([]cinder.Volume, erro vs, err := cinder.ExtractVolumes(allPages) if err != nil { - return false, fmt.Errorf("error extracting volumes: %v", err) + return false, fmt.Errorf("error extracting volumes from pages: %v", err) } volumes = vs return true, nil @@ -197,5 +201,4 @@ func (c *openstackCloud) CreateVolume(opt cinder.CreateOpts) (*cinder.Volume, er } else { return volume, wait.ErrWaitTimeout } - } diff --git a/upup/pkg/fi/cloudup/utils.go b/upup/pkg/fi/cloudup/utils.go index 2b207902d6f96..5d203bf212229 100644 --- a/upup/pkg/fi/cloudup/utils.go +++ b/upup/pkg/fi/cloudup/utils.go @@ -133,7 +133,8 @@ func BuildCloud(cluster *kops.Cluster) (fi.Cloud, error) { } case kops.CloudProviderOpenstack: { - osc, err := openstack.NewOpenstackCloud() + cloudTags := map[string]string{openstack.TagClusterName: cluster.ObjectMeta.Name} + osc, err := openstack.NewOpenstackCloud(cloudTags) if err != nil { return nil, err }