server groups, lb, instance, and dns tasks, models and resources

This commit is contained in:
Jon Perritt 2018-09-11 08:17:26 -05:00 committed by Derek Lemon -T (delemon - AEROTEK INC at Cisco)
parent 83365a992a
commit 3064f6be15
27 changed files with 1904 additions and 58 deletions

View File

@ -5,12 +5,16 @@ go_library(
srcs = [
"context.go",
"convenience.go",
"instance.go",
"lb.go",
"network.go",
"servergroup.go",
"sshkey.go",
],
importpath = "k8s.io/kops/pkg/model/openstackmodel",
visibility = ["//visibility:public"],
deps = [
"//pkg/apis/kops:go_default_library",
"//pkg/model:go_default_library",
"//upup/pkg/fi:go_default_library",
"//upup/pkg/fi/cloudup/openstacktasks:go_default_library",

View File

@ -36,3 +36,7 @@ func (c *OpenstackModelContext) LinkToRouter(name *string) *openstacktasks.Route
func (c *OpenstackModelContext) LinkToSubnet(name *string) *openstacktasks.Subnet {
return &openstacktasks.Subnet{Name: name}
}
func (c *OpenstackModelContext) LinkToPort(name *string) *openstacktasks.Port {
return &openstacktasks.Port{Name: name}
}

View File

@ -0,0 +1,76 @@
/*
Copyright 2018 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 openstackmodel
import (
"strings"
"k8s.io/kops/pkg/model"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup/openstacktasks"
)
// InstanceModelBuilder configures instance objects
type InstanceModelBuilder struct {
*OpenstackModelContext
BootstrapScript *model.BootstrapScript
Lifecycle *fi.Lifecycle
}
var _ fi.ModelBuilder = &InstanceModelBuilder{}
func (b *InstanceModelBuilder) Build(c *fi.ModelBuilderContext) error {
sshKeyNameFull, err := b.SSHKeyName()
if err != nil {
return err
}
splitSSHKeyNameFull := strings.Split(sshKeyNameFull, "-")
sshKeyName := splitSSHKeyNameFull[0]
clusterTag := "KubernetesCluster:" + strings.Replace(b.ClusterName(), ".", "-", -1)
// In the future, OpenStack will use Machine API to manage groups,
// for now create d.InstanceGroups.Spec.MinSize amount of servers
for _, ig := range b.InstanceGroups {
clusterName := b.AutoscalingGroupName(ig)
{
t := &openstacktasks.Port{
Name: s(clusterName),
Network: b.LinkToNetwork(),
Lifecycle: b.Lifecycle,
}
c.AddTask(t)
}
{
var t openstacktasks.Instance
t.Count = int(fi.Int32Value(ig.Spec.MinSize))
t.Name = fi.String(clusterName)
t.Region = fi.String(b.Cluster.Spec.Subnets[0].Region)
t.Flavor = fi.String(ig.Spec.MachineType)
t.Image = fi.String(ig.Spec.Image)
t.SSHKey = fi.String(sshKeyName)
t.Tags = []string{clusterTag}
t.Role = fi.String(string(ig.Spec.Role))
t.Port = b.LinkToPort(fi.String(clusterName))
c.AddTask(&t)
}
}
return nil
}

View File

@ -0,0 +1,72 @@
/*
Copyright 2018 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 openstackmodel
import (
"fmt"
"strings"
"k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup/openstacktasks"
)
// APILBModelBuilder configures loadbalancer objects
type APILBModelBuilder struct {
*OpenstackModelContext
Lifecycle *fi.Lifecycle
}
var _ fi.ModelBuilder = &APILBModelBuilder{}
func (b *APILBModelBuilder) Build(c *fi.ModelBuilderContext) error {
if !b.UseLoadBalancerForAPI() {
return nil
}
lbSpec := b.Cluster.Spec.API.LoadBalancer
if lbSpec == nil {
// Skipping API LB creation; not requested in Spec
return nil
}
switch lbSpec.Type {
case kops.LoadBalancerTypePublic:
// OK
case kops.LoadBalancerTypeInternal:
return fmt.Errorf("internal loadbalancers are not yet supported by kops on openstack")
default:
return fmt.Errorf("unhandled loadbalancers type %q", lbSpec.Type)
}
clusterName := b.ClusterName()
lbName := "api-" + strings.Replace(clusterName, ".", "-", -1)
{
t := &openstacktasks.LB{
Name: s(lbName),
Subnet: b.LinkToSubnet(s(b.Cluster.Spec.Subnets[0].Name)),
Lifecycle: b.Lifecycle,
}
c.AddTask(t)
}
return nil
}

View File

@ -0,0 +1,47 @@
/*
Copyright 2018 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 openstackmodel
import (
"fmt"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup/openstacktasks"
)
// ServerGroupModelBuilder configures server group objects
type ServerGroupModelBuilder struct {
*OpenstackModelContext
Lifecycle *fi.Lifecycle
}
var _ fi.ModelBuilder = &ServerGroupModelBuilder{}
func (b *ServerGroupModelBuilder) Build(c *fi.ModelBuilderContext) error {
clusterName := b.ClusterName()
for _, ig := range b.InstanceGroups {
t := &openstacktasks.ServerGroup{
Name: s(fmt.Sprintf("%s-%s", clusterName, ig.Spec.Role)),
Policies: []string{"anti-affinity"},
Lifecycle: b.Lifecycle,
}
c.AddTask(t)
}
return nil
}

View File

@ -2,11 +2,29 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["resources.go"],
srcs = [
"blockstorage.go",
"compute.go",
"dns.go",
"lb.go",
"networking.go",
"openstack.go",
],
importpath = "k8s.io/kops/pkg/resources/openstack",
visibility = ["//visibility:public"],
deps = [
"//pkg/resources:go_default_library",
"//upup/pkg/fi:go_default_library",
"//upup/pkg/fi/cloudup/openstack:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/networks:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/ports:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/subnets:go_default_library",
],
)

View File

@ -0,0 +1,61 @@
/*
Copyright 2018 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 openstack
import (
"fmt"
"github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes"
"k8s.io/kops/pkg/resources"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup/openstack"
)
const (
typeVolume = "Volume"
)
var listBlockStorageFunctions = []listFn{
listVolumes,
}
func listVolumes(cloud openstack.OpenstackCloud, clusterName string) ([]*resources.Resource, error) {
var resourceTrackers []*resources.Resource
opts := volumes.ListOpts{
Metadata: cloud.GetCloudTags(),
}
vs, err := cloud.ListVolumes(opts)
if err != nil {
return nil, fmt.Errorf("failed to list volumes: %s", err)
}
for _, v := range vs {
resourceTracker := &resources.Resource{
Name: v.Name,
ID: v.ID,
Type: typeVolume,
Deleter: func(cloud fi.Cloud, r *resources.Resource) error {
return volumes.Delete(cloud.(openstack.OpenstackCloud).BlockStorageClient(), r.ID).ExtractErr()
},
Obj: v,
}
resourceTrackers = append(resourceTrackers, resourceTracker)
}
return resourceTrackers, nil
}

View File

@ -0,0 +1,124 @@
/*
Copyright 2018 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 openstack
import (
"fmt"
"strings"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups"
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
"k8s.io/kops/pkg/resources"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup/openstack"
)
const (
typeKeypair = "keypair"
typeServer = "server"
typeServerGroup = "serverGroup"
)
func listInstances(cloud openstack.OpenstackCloud, clusterName string) ([]*resources.Resource, error) {
var resourceTrackers []*resources.Resource
clusterTag := "KubernetesCluster:" + strings.Replace(clusterName, ".", "-", -1)
opts := servers.ListOpts{
Name: clusterTag,
}
ss, err := cloud.ListInstances(opts)
if err != nil {
return nil, fmt.Errorf("failed to list servers: %v", err)
}
for _, s := range ss {
resourceTracker := &resources.Resource{
Name: s.Name,
ID: s.ID,
Type: typeServer,
Deleter: func(cloud fi.Cloud, r *resources.Resource) error {
return servers.Delete(cloud.(openstack.OpenstackCloud).ComputeClient(), s.ID).ExtractErr()
},
Obj: s,
}
resourceTrackers = append(resourceTrackers, resourceTracker)
}
return resourceTrackers, nil
}
func listServerGroups(cloud openstack.OpenstackCloud, clusterName string) ([]*resources.Resource, error) {
var resourceTrackers []*resources.Resource
sgsAll, err := cloud.ListServerGroups()
if err != nil {
return nil, fmt.Errorf("failed to extract server group pages: %s", err)
}
var sgs []servergroups.ServerGroup
for _, sg := range sgsAll {
if strings.HasPrefix(sg.Name, clusterName) {
sgs = append(sgs, sg)
}
}
for _, sg := range sgs {
resourceTracker := &resources.Resource{
Name: sg.ID,
ID: sg.Name,
Type: typeServerGroup,
Deleter: func(cloud fi.Cloud, r *resources.Resource) error {
return servergroups.Delete(cloud.(openstack.OpenstackCloud).ComputeClient(), r.ID).ExtractErr()
},
Obj: sg,
}
resourceTrackers = append(resourceTrackers, resourceTracker)
}
return resourceTrackers, nil
}
func listKeypairs(cloud openstack.OpenstackCloud, clusterName string) ([]*resources.Resource, error) {
var resourceTrackers []*resources.Resource
kp, err := cloud.ListKeypair(clusterName)
if err != nil {
return nil, fmt.Errorf("failed to get keypair: %s", err)
}
if kp == nil {
return resourceTrackers, nil
}
resourceTracker := &resources.Resource{
Name: kp.Name,
ID: kp.Name,
Type: typeKeypair,
Deleter: func(cloud fi.Cloud, r *resources.Resource) error {
return keypairs.Delete(cloud.(openstack.OpenstackCloud).ComputeClient(), r.ID).ExtractErr()
},
Obj: kp,
}
resourceTrackers = append(resourceTrackers, resourceTracker)
return resourceTrackers, nil
}

View File

@ -0,0 +1,79 @@
/*
Copyright 2018 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 openstack
import (
"fmt"
"github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets"
"github.com/gophercloud/gophercloud/openstack/dns/v2/zones"
"k8s.io/kops/pkg/resources"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup/openstack"
)
const (
typeDNSRecord = "dNSRecord"
)
var listDNSFunctions = []listFn{
listDNSRecordsets,
}
func listDNSRecordsets(cloud openstack.OpenstackCloud, clusterName string) ([]*resources.Resource, error) {
zopts := zones.ListOpts{
Name: clusterName,
}
zs, err := cloud.ListDNSZones(zopts)
if err != nil {
return nil, fmt.Errorf("failed to list dns zones: %s", err)
}
switch len(zs) {
case 0:
case 1:
default:
}
z := zs[0]
rrs, err := cloud.ListDNSRecordsets(z.ID, nil)
if err != nil {
return nil, fmt.Errorf("failed to extract recordsets pages for zone %s: %v", z.Name, err)
}
var resourceTrackers []*resources.Resource
for _, rr := range rrs {
if rr.Type != "A" {
continue
}
resourceTracker := &resources.Resource{
Name: rr.Name,
ID: rr.ID,
Type: typeDNSRecord,
Deleter: func(cloud fi.Cloud, r *resources.Resource) error {
return recordsets.Delete(cloud.(openstack.OpenstackCloud).DNSClient(), z.ID, rr.ID).ExtractErr()
},
Obj: rr,
}
resourceTrackers = append(resourceTrackers, resourceTracker)
}
return resourceTrackers, nil
}

View File

@ -0,0 +1,59 @@
/*
Copyright 2018 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 openstack
import (
"fmt"
"github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers"
"k8s.io/kops/pkg/resources"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup/openstack"
)
const (
typeLB = "lb"
)
func listLBs(cloud openstack.OpenstackCloud, clusterName string) ([]*resources.Resource, error) {
opts := loadbalancers.ListOpts{
Name: clusterName,
}
lbs, err := cloud.ListLBs(opts)
if err != nil {
return nil, fmt.Errorf("failed to list lbs: %s", err)
}
var rts []*resources.Resource
for _, t := range lbs {
rt := &resources.Resource{
Name: t.Name,
ID: t.ID,
Type: typeLB,
Deleter: func(cloud fi.Cloud, r *resources.Resource) error {
opts := loadbalancers.DeleteOpts{
Cascade: true,
}
return loadbalancers.Delete(cloud.(openstack.OpenstackCloud).LoadBalancerClient(), t.ID, opts).ExtractErr()
},
Obj: lbs,
}
rts = append(rts, rt)
}
return rts, nil
}

View File

@ -0,0 +1,107 @@
/*
Copyright 2018 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 openstack
import (
"fmt"
"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
"k8s.io/kops/pkg/resources"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup/openstack"
)
const (
typeNetwork = "network"
typeSubnet = "subnet"
typePort = "port"
typeRouter = "router"
)
func listNetworks(cloud openstack.OpenstackCloud, clusterName string) ([]*resources.Resource, error) {
rts := make([]*resources.Resource, 0)
opts := networks.ListOpts{
Name: clusterName,
}
ns, err := cloud.ListNetworks(opts)
if err != nil {
return nil, fmt.Errorf("failed to list networks: %s", err)
}
for _, n := range ns {
rt := &resources.Resource{
Name: n.Name,
ID: n.ID,
Type: typeNetwork,
Deleter: func(cloud fi.Cloud, r *resources.Resource) error {
return networks.Delete(cloud.(openstack.OpenstackCloud).NetworkingClient(), n.ID).ExtractErr()
},
Obj: n,
}
rts = append(rts, rt)
}
return rts, nil
}
func listSubnets(cloud openstack.OpenstackCloud, clusterName string) ([]*resources.Resource, error) {
rts := make([]*resources.Resource, 0)
opts := subnets.ListOpts{
Name: clusterName,
}
subs, err := cloud.ListSubnets(opts)
if err != nil {
return nil, fmt.Errorf("failed to list subnets: %s", err)
}
for _, s := range subs {
rt := &resources.Resource{
Name: s.Name,
ID: s.ID,
Type: typeSubnet,
Deleter: func(cloud fi.Cloud, r *resources.Resource) error {
return subnets.Delete(cloud.(openstack.OpenstackCloud).NetworkingClient(), s.ID).ExtractErr()
},
Obj: s,
}
rts = append(rts, rt)
}
return rts, nil
}
func listPorts(cloud openstack.OpenstackCloud, clusterName string) ([]*resources.Resource, error) {
rts := make([]*resources.Resource, 0)
opts := ports.ListOpts{
Name: clusterName,
}
ss, err := cloud.ListPorts(opts)
if err != nil {
return nil, fmt.Errorf("failed to list ports: %s", err)
}
for _, s := range ss {
rt := &resources.Resource{
Name: s.Name,
ID: s.ID,
Type: typeSubnet,
Deleter: func(cloud fi.Cloud, r *resources.Resource) error {
return ports.Delete(cloud.(openstack.OpenstackCloud).NetworkingClient(), s.ID).ExtractErr()
},
Obj: s,
}
rts = append(rts, rt)
}
return rts, nil
}

View File

@ -0,0 +1,80 @@
/*
Copyright 2018 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 openstack
import (
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups"
"github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers"
"k8s.io/kops/pkg/resources"
"k8s.io/kops/upup/pkg/fi/cloudup/openstack"
)
type listFn func(openstack.OpenstackCloud, string) ([]*resources.Resource, error)
// ListResources lists the OpenStack resources kops manages
func ListResources(cloud openstack.OpenstackCloud, clusterName string) (map[string]*resources.Resource, error) {
rts := make(map[string]*resources.Resource)
keypairs, err := listKeypairs(cloud, clusterName)
if err != nil {
return rts, err
}
keypairIDs := make([]string, len(keypairs))
for i, t := range keypairs {
id := t.Type + ":" + t.ID
rts[id] = t
keypairIDs[i] = id
}
serverGroups, err := listServerGroups(cloud, clusterName)
if err != nil {
return rts, err
}
serverGroupIDs := make([]string, len(serverGroups))
for _, t := range serverGroups {
id := t.Type + ":" + t.ID
for _, m := range t.Obj.(servergroups.ServerGroup).Members {
t.Blocked = append(t.Blocks, typeServer+":"+m)
}
serverGroupIDs = append(serverGroupIDs, id)
rts[id] = t
}
instances, err := listInstances(cloud, clusterName)
if err != nil {
return rts, err
}
for _, t := range instances {
rts[t.Type+":"+t.ID] = t
}
lbs, err := listLBs(cloud, clusterName)
if err != nil {
return rts, err
}
for _, t := range lbs {
listeners := t.Obj.(loadbalancers.LoadBalancer).Listeners
for _, l := range listeners {
for _, p := range l.Pools {
t.Blocks = append(t.Blocks, typeSubnet+":"+p.SubnetID)
}
}
rts[t.Type+":"+t.ID] = t
}
return rts, nil
}

View File

@ -1,32 +0,0 @@
/*
Copyright 2018 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 openstack
import (
"k8s.io/kops/pkg/resources"
"k8s.io/kops/upup/pkg/fi/cloudup/openstack"
)
type listFn func(openstack.OpenstackCloud, string) ([]*resources.Resource, error)
func ListResources(cloud openstack.OpenstackCloud, clusterName string) (map[string]*resources.Resource, error) {
resourceTrackers := make(map[string]*resources.Resource)
// TODO(lmb): Implement resource list
return resourceTrackers, nil
}

View File

@ -518,10 +518,20 @@ func (c *ApplyClusterCmd) Run() error {
region = osCloud.Region()
l.AddTypes(map[string]interface{}{
"sshKey": &openstacktasks.SSHKey{},
// Compute
"sshKey": &openstacktasks.SSHKey{},
"serverGroup": &openstacktasks.ServerGroup{},
"instance": &openstacktasks.Instance{},
// Networking
"network": &openstacktasks.Network{},
"router": &openstacktasks.Router{},
"network": &openstacktasks.Network{},
"subnet": &openstacktasks.Subnet{},
"router": &openstacktasks.Router{},
"securityGroup": &openstacktasks.SecurityGroup{},
"securityGroupRule": &openstacktasks.SecurityGroupRule{},
// BlockStorage
"volume": &openstacktasks.Volume{},
// LB
"lb": &openstacktasks.LB{},
})
if len(sshPublicKeys) == 0 {
@ -679,8 +689,10 @@ func (c *ApplyClusterCmd) Run() error {
}
l.Builders = append(l.Builders,
&openstackmodel.APILBModelBuilder{OpenstackModelContext: openstackModelContext, Lifecycle: &clusterLifecycle},
&openstackmodel.NetworkModelBuilder{OpenstackModelContext: openstackModelContext, Lifecycle: &networkLifecycle},
&openstackmodel.SSHKeyModelBuilder{OpenstackModelContext: openstackModelContext, Lifecycle: &securityLifecycle},
&openstackmodel.ServerGroupModelBuilder{OpenstackModelContext: openstackModelContext, Lifecycle: &clusterLifecycle},
)
default:
@ -780,6 +792,15 @@ func (c *ApplyClusterCmd) Run() error {
// BareMetal tasks will go here
case kops.CloudProviderOpenstack:
openstackModelContext := &openstackmodel.OpenstackModelContext{
KopsModelContext: modelContext,
}
l.Builders = append(l.Builders, &openstackmodel.InstanceModelBuilder{
OpenstackModelContext: openstackModelContext,
//BootstrapScript: bootstrapScriptBuilder,
Lifecycle: &clusterLifecycle,
})
default:
return fmt.Errorf("unknown cloudprovider %q", cluster.Spec.CloudProvider)

View File

@ -10,6 +10,7 @@ go_library(
visibility = ["//visibility:public"],
deps = [
"//dnsprovider/pkg/dnsprovider:go_default_library",
"//dnsprovider/pkg/dnsprovider/providers/openstack/designate:go_default_library",
"//pkg/apis/kops:go_default_library",
"//pkg/cloudinstances:go_default_library",
"//upup/pkg/fi:go_default_library",
@ -20,6 +21,10 @@ go_library(
"//vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules:go_default_library",

View File

@ -17,7 +17,9 @@ limitations under the License.
package openstack
import (
"crypto/tls"
"fmt"
"net/http"
"time"
"github.com/golang/glog"
@ -26,6 +28,10 @@ import (
cinder "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups"
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
"github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets"
"github.com/gophercloud/gophercloud/openstack/dns/v2/zones"
"github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers"
sg "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups"
sgr "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules"
@ -35,6 +41,7 @@ import (
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/kops/dnsprovider/pkg/dnsprovider"
"k8s.io/kops/dnsprovider/pkg/dnsprovider/providers/openstack/designate"
"k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/pkg/cloudinstances"
"k8s.io/kops/upup/pkg/fi"
@ -67,9 +74,19 @@ var writeBackoff = wait.Backoff{
type OpenstackCloud interface {
fi.Cloud
ComputeClient() *gophercloud.ServiceClient
BlockStorageClient() *gophercloud.ServiceClient
NetworkingClient() *gophercloud.ServiceClient
LoadBalancerClient() *gophercloud.ServiceClient
DNSClient() *gophercloud.ServiceClient
// Region returns the region which cloud will run on
Region() string
ListInstances(servers.ListOptsBuilder) ([]servers.Server, error)
CreateInstance(servers.CreateOptsBuilder) (*servers.Server, error)
// SetVolumeTags will set the tags for the Cinder volume
SetVolumeTags(id string, tags map[string]string) error
@ -77,22 +94,22 @@ type OpenstackCloud interface {
GetCloudTags() map[string]string
// ListVolumes will return the Cinder volumes which match the options
ListVolumes(opt cinder.ListOpts) ([]cinder.Volume, error)
ListVolumes(opt cinder.ListOptsBuilder) ([]cinder.Volume, error)
// CreateVolume will create a new Cinder Volume
CreateVolume(opt cinder.CreateOpts) (*cinder.Volume, error)
CreateVolume(opt cinder.CreateOptsBuilder) (*cinder.Volume, error)
//ListSecurityGroups will return the Neutron security groups which match the options
ListSecurityGroups(opt sg.ListOpts) ([]sg.SecGroup, error)
//CreateSecurityGroup will create a new Neutron security group
CreateSecurityGroup(opt sg.CreateOpts) (*sg.SecGroup, error)
CreateSecurityGroup(opt sg.CreateOptsBuilder) (*sg.SecGroup, error)
//ListSecurityGroupRules will return the Neutron security group rules which match the options
ListSecurityGroupRules(opt sgr.ListOpts) ([]sgr.SecGroupRule, error)
//CreateSecurityGroupRule will create a new Neutron security group rule
CreateSecurityGroupRule(opt sgr.CreateOpts) (*sgr.SecGroupRule, error)
CreateSecurityGroupRule(opt sgr.CreateOptsBuilder) (*sgr.SecGroupRule, error)
//ListNetworks will return the Neutron networks which match the options
ListNetworks(opt networks.ListOptsBuilder) ([]networks.Network, error)
@ -118,6 +135,8 @@ type OpenstackCloud interface {
// CreateKeypair will create a new Nova Keypair
CreateKeypair(opt keypairs.CreateOptsBuilder) (*keypairs.KeyPair, error)
CreatePort(opt ports.CreateOptsBuilder) (*ports.Port, error)
//ListPorts will return the Neutron ports which match the options
ListPorts(opt ports.ListOptsBuilder) ([]ports.Port, error)
@ -125,13 +144,28 @@ type OpenstackCloud interface {
CreateRouterInterface(routerID string, opt routers.AddInterfaceOptsBuilder) (*routers.InterfaceInfo, error)
// CreateServerGroup will create a new server group.
CreateServerGroup(opt servergroups.CreateOpts) (*servergroups.ServerGroup, error)
CreateServerGroup(opt servergroups.CreateOptsBuilder) (*servergroups.ServerGroup, error)
// ListServerGroups will list available server groups
ListServerGroups() ([]servergroups.ServerGroup, error)
// ListDNSZones will list available DNS zones
ListDNSZones(opt zones.ListOptsBuilder) ([]zones.Zone, error)
// ListDNSRecordsets will list the DNS recordsets for the given zone id
ListDNSRecordsets(zoneID string, opt recordsets.ListOptsBuilder) ([]recordsets.RecordSet, error)
CreateLB(opt loadbalancers.CreateOptsBuilder) (*loadbalancers.LoadBalancer, error)
ListLBs(opt loadbalancers.ListOptsBuilder) ([]loadbalancers.LoadBalancer, error)
}
type openstackCloud struct {
cinderClient *gophercloud.ServiceClient
neutronClient *gophercloud.ServiceClient
novaClient *gophercloud.ServiceClient
dnsClient *gophercloud.ServiceClient
lbClient *gophercloud.ServiceClient
tags map[string]string
region string
}
@ -145,7 +179,28 @@ func NewOpenstackCloud(tags map[string]string) (OpenstackCloud, error) {
if err != nil {
return nil, err
}
provider, err := os.AuthenticatedClient(authOption)
/*
provider, err := os.AuthenticatedClient(authOption)
if err != nil {
return nil, fmt.Errorf("error building openstack authenticated client: %v", err)
}*/
provider, err := os.NewClient(authOption.IdentityEndpoint)
if err != nil {
return nil, fmt.Errorf("error building openstack provider client: %v", err)
}
tlsconfig := &tls.Config{}
tlsconfig.InsecureSkipVerify = true
transport := &http.Transport{TLSClientConfig: tlsconfig}
provider.HTTPClient = http.Client{
Transport: transport,
}
glog.V(2).Info("authenticating to keystone")
err = os.Authenticate(provider, authOption)
if err != nil {
return nil, fmt.Errorf("error building openstack authenticated client: %v", err)
}
@ -177,18 +232,55 @@ func NewOpenstackCloud(tags map[string]string) (OpenstackCloud, error) {
return nil, fmt.Errorf("error building nova client: %v", err)
}
endpointOpt, err = config.GetServiceConfig("Designate")
if err != nil {
return nil, err
}
dnsClient, err := os.NewDNSV2(provider, endpointOpt)
if err != nil {
return nil, fmt.Errorf("error building dns client: %v", err)
}
endpointOpt, err = config.GetServiceConfig("LB")
if err != nil {
return nil, err
}
lbClient, err := os.NewLoadBalancerV2(provider, endpointOpt)
if err != nil {
return nil, fmt.Errorf("error building lb client: %v", err)
}
region := endpointOpt.Region
c := &openstackCloud{
cinderClient: cinderClient,
neutronClient: neutronClient,
novaClient: novaClient,
lbClient: lbClient,
dnsClient: dnsClient,
tags: tags,
region: region,
}
return c, nil
}
func (c *openstackCloud) ComputeClient() *gophercloud.ServiceClient {
return c.novaClient
}
func (c *openstackCloud) BlockStorageClient() *gophercloud.ServiceClient {
return c.cinderClient
}
func (c *openstackCloud) NetworkingClient() *gophercloud.ServiceClient {
return c.neutronClient
}
func (c *openstackCloud) LoadBalancerClient() *gophercloud.ServiceClient {
return c.lbClient
}
func (c *openstackCloud) DNSClient() *gophercloud.ServiceClient {
return c.dnsClient
}
func (c *openstackCloud) Region() string {
return c.region
}
@ -198,7 +290,11 @@ func (c *openstackCloud) ProviderID() kops.CloudProviderID {
}
func (c *openstackCloud) DNS() (dnsprovider.Interface, error) {
return nil, fmt.Errorf("openstackCloud::DNS not implemented")
provider, err := dnsprovider.GetDnsProvider(designate.ProviderName, nil)
if err != nil {
return nil, fmt.Errorf("Error building (Designate) DNS provider: %v", err)
}
return provider, nil
}
func (c *openstackCloud) FindVPCInfo(id string) (*fi.VPCInfo, error) {
@ -217,6 +313,51 @@ func (c *openstackCloud) GetCloudGroups(cluster *kops.Cluster, instancegroups []
return nil, fmt.Errorf("openstackCloud::GetCloudGroups not implemented")
}
func (c *openstackCloud) ListInstances(opt servers.ListOptsBuilder) ([]servers.Server, error) {
var instances []servers.Server
done, err := vfs.RetryWithBackoff(readBackoff, func() (bool, error) {
allPages, err := servers.List(c.novaClient, opt).AllPages()
if err != nil {
return false, fmt.Errorf("error listing servers %v: %v", opt, err)
}
ss, err := servers.ExtractServers(allPages)
if err != nil {
return false, fmt.Errorf("error extracting servers from pages: %v", err)
}
instances = ss
return true, nil
})
if err != nil {
return instances, err
} else if done {
return instances, nil
} else {
return instances, wait.ErrWaitTimeout
}
}
func (c *openstackCloud) CreateInstance(opt servers.CreateOptsBuilder) (*servers.Server, error) {
var server *servers.Server
done, err := vfs.RetryWithBackoff(writeBackoff, func() (bool, error) {
v, err := servers.Create(c.novaClient, opt).Extract()
if err != nil {
return false, fmt.Errorf("error creating server %v: %v", opt, err)
}
server = v
return true, nil
})
if err != nil {
return server, err
} else if done {
return server, nil
} else {
return server, wait.ErrWaitTimeout
}
}
func (c *openstackCloud) SetVolumeTags(id string, tags map[string]string) error {
if len(tags) == 0 {
return nil
@ -247,7 +388,7 @@ func (c *openstackCloud) GetCloudTags() map[string]string {
return c.tags
}
func (c *openstackCloud) ListVolumes(opt cinder.ListOpts) ([]cinder.Volume, error) {
func (c *openstackCloud) ListVolumes(opt cinder.ListOptsBuilder) ([]cinder.Volume, error) {
var volumes []cinder.Volume
done, err := vfs.RetryWithBackoff(readBackoff, func() (bool, error) {
@ -272,7 +413,7 @@ func (c *openstackCloud) ListVolumes(opt cinder.ListOpts) ([]cinder.Volume, erro
}
}
func (c *openstackCloud) CreateVolume(opt cinder.CreateOpts) (*cinder.Volume, error) {
func (c *openstackCloud) CreateVolume(opt cinder.CreateOptsBuilder) (*cinder.Volume, error) {
var volume *cinder.Volume
done, err := vfs.RetryWithBackoff(writeBackoff, func() (bool, error) {
@ -317,7 +458,7 @@ func (c *openstackCloud) ListSecurityGroups(opt sg.ListOpts) ([]sg.SecGroup, err
}
}
func (c *openstackCloud) CreateSecurityGroup(opt sg.CreateOpts) (*sg.SecGroup, error) {
func (c *openstackCloud) CreateSecurityGroup(opt sg.CreateOptsBuilder) (*sg.SecGroup, error) {
var group *sg.SecGroup
done, err := vfs.RetryWithBackoff(writeBackoff, func() (bool, error) {
@ -362,7 +503,7 @@ func (c *openstackCloud) ListSecurityGroupRules(opt sgr.ListOpts) ([]sgr.SecGrou
}
}
func (c *openstackCloud) CreateSecurityGroupRule(opt sgr.CreateOpts) (*sgr.SecGroupRule, error) {
func (c *openstackCloud) CreateSecurityGroupRule(opt sgr.CreateOptsBuilder) (*sgr.SecGroupRule, error) {
var rule *sgr.SecGroupRule
done, err := vfs.RetryWithBackoff(writeBackoff, func() (bool, error) {
@ -559,6 +700,26 @@ func (c *openstackCloud) CreateKeypair(opt keypairs.CreateOptsBuilder) (*keypair
}
}
func (c *openstackCloud) CreatePort(opt ports.CreateOptsBuilder) (*ports.Port, error) {
var p *ports.Port
done, err := vfs.RetryWithBackoff(writeBackoff, func() (bool, error) {
v, err := ports.Create(c.neutronClient, opt).Extract()
if err != nil {
return false, fmt.Errorf("error creating port: %v", err)
}
p = v
return true, nil
})
if err != nil {
return p, err
} else if done {
return p, nil
} else {
return p, wait.ErrWaitTimeout
}
}
func (c *openstackCloud) ListPorts(opt ports.ListOptsBuilder) ([]ports.Port, error) {
var p []ports.Port
@ -604,6 +765,142 @@ func (c *openstackCloud) CreateRouterInterface(routerID string, opt routers.AddI
}
}
func (c *openstackCloud) CreateServerGroup(opt servergroups.CreateOpts) (*servergroups.ServerGroup, error) {
return nil, fmt.Errorf("openstackCloud::CreateServerGroup not implemented")
func (c *openstackCloud) CreateServerGroup(opt servergroups.CreateOptsBuilder) (*servergroups.ServerGroup, error) {
var i *servergroups.ServerGroup
done, err := vfs.RetryWithBackoff(writeBackoff, func() (bool, error) {
v, err := servergroups.Create(c.novaClient, opt).Extract()
if err != nil {
return false, fmt.Errorf("error creating server group: %v", err)
}
i = v
return true, nil
})
if err != nil {
return i, err
} else if done {
return i, nil
} else {
return i, wait.ErrWaitTimeout
}
}
func (c *openstackCloud) ListServerGroups() ([]servergroups.ServerGroup, error) {
var sgs []servergroups.ServerGroup
done, err := vfs.RetryWithBackoff(readBackoff, func() (bool, error) {
allPages, err := servergroups.List(c.novaClient).AllPages()
if err != nil {
return false, fmt.Errorf("error listing server groups: %v", err)
}
r, err := servergroups.ExtractServerGroups(allPages)
if err != nil {
return false, fmt.Errorf("error extracting server groups from pages: %v", err)
}
sgs = r
return true, nil
})
if err != nil {
return sgs, err
} else if done {
return sgs, nil
} else {
return sgs, wait.ErrWaitTimeout
}
}
// ListDNSZones will list available DNS zones
func (c *openstackCloud) ListDNSZones(opt zones.ListOptsBuilder) ([]zones.Zone, error) {
var zs []zones.Zone
done, err := vfs.RetryWithBackoff(readBackoff, func() (bool, error) {
allPages, err := zones.List(c.dnsClient, opt).AllPages()
if err != nil {
return false, fmt.Errorf("failed to list dns zones: %s", err)
}
r, err := zones.ExtractZones(allPages)
if err != nil {
return false, fmt.Errorf("failed to extract dns zone pages: %s", err)
}
zs = r
return true, nil
})
if err != nil {
return zs, err
} else if done {
return zs, nil
} else {
return zs, wait.ErrWaitTimeout
}
}
// ListDNSRecordsets will list DNS recordsets
func (c *openstackCloud) ListDNSRecordsets(zoneID string, opt recordsets.ListOptsBuilder) ([]recordsets.RecordSet, error) {
var rrs []recordsets.RecordSet
done, err := vfs.RetryWithBackoff(readBackoff, func() (bool, error) {
allPages, err := recordsets.ListByZone(c.dnsClient, zoneID, opt).AllPages()
if err != nil {
return false, fmt.Errorf("failed to list dns recordsets: %s", err)
}
r, err := recordsets.ExtractRecordSets(allPages)
if err != nil {
return false, fmt.Errorf("failed to extract dns recordsets pages: %s", err)
}
rrs = r
return true, nil
})
if err != nil {
return rrs, err
} else if done {
return rrs, nil
} else {
return rrs, wait.ErrWaitTimeout
}
}
func (c *openstackCloud) CreateLB(opt loadbalancers.CreateOptsBuilder) (*loadbalancers.LoadBalancer, error) {
var i *loadbalancers.LoadBalancer
done, err := vfs.RetryWithBackoff(writeBackoff, func() (bool, error) {
v, err := loadbalancers.Create(c.lbClient, opt).Extract()
if err != nil {
return false, fmt.Errorf("error creating loadbalancer: %v", err)
}
i = v
return true, nil
})
if err != nil {
return i, err
} else if done {
return i, nil
} else {
return i, wait.ErrWaitTimeout
}
}
// ListLBs will list load balancers
func (c *openstackCloud) ListLBs(opt loadbalancers.ListOptsBuilder) ([]loadbalancers.LoadBalancer, error) {
var lbs []loadbalancers.LoadBalancer
done, err := vfs.RetryWithBackoff(readBackoff, func() (bool, error) {
allPages, err := loadbalancers.List(c.lbClient, opt).AllPages()
if err != nil {
return false, fmt.Errorf("failed to list loadbalancers: %s", err)
}
r, err := loadbalancers.ExtractLoadBalancers(allPages)
if err != nil {
return false, fmt.Errorf("failed to extract loadbalancer pages: %s", err)
}
lbs = r
return true, nil
})
if err != nil {
return lbs, err
} else if done {
return lbs, nil
} else {
return lbs, wait.ErrWaitTimeout
}
}

View File

@ -4,8 +4,13 @@ go_library(
name = "go_default_library",
srcs = [
"instance.go",
"instance_fitask.go",
"lb.go",
"lb_fitask.go",
"network.go",
"network_fitask.go",
"port.go",
"port_fitask.go",
"router.go",
"router_fitask.go",
"routerinterface.go",
@ -13,6 +18,8 @@ go_library(
"securitygroup.go",
"securitygroup_fitask.go",
"securitygrouprule.go",
"servergroup.go",
"servergroup_fitask.go",
"sshkey.go",
"sshkey_fitask.go",
"subnet.go",
@ -30,6 +37,11 @@ go_library(
"//vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/schedulerhints:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules:go_default_library",

View File

@ -16,7 +16,116 @@ limitations under the License.
package openstacktasks
import "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/schedulerhints"
import (
"fmt"
// TODO(lmb): Remove after implementing. Using this to pull in the vendor dep.
var _ schedulerhints.SchedulerHints
"github.com/golang/glog"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/schedulerhints"
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup/openstack"
)
//go:generate fitask -type=Instance
type Instance struct {
ID *string
Name *string
Port *Port
Region *string
Flavor *string
Image *string
SSHKey *string
Tags []string
Count int
Role *string
Lifecycle *fi.Lifecycle
}
var _ fi.CompareWithID = &Instance{}
func (e *Instance) CompareWithID() *string {
return e.ID
}
func (e *Instance) Find(c *fi.Context) (*Instance, error) {
if e == nil || e.ID == nil {
return nil, nil
}
id := *(e.ID)
v, err := servers.Get(c.Cloud.(openstack.OpenstackCloud).ComputeClient(), id).Extract()
if err != nil {
return nil, fmt.Errorf("error finding server with id %s: %v", id, err)
}
a := new(Instance)
a.ID = fi.String(v.ID)
a.Name = fi.String(v.Name)
a.SSHKey = fi.String(v.KeyName)
a.Lifecycle = e.Lifecycle
return a, nil
}
func (e *Instance) Run(c *fi.Context) error {
return fi.DefaultDeltaRunMethod(e, c)
}
func (_ *Instance) CheckChanges(a, e, changes *Instance) error {
if a == nil {
if e.Name == nil {
return fi.RequiredField("Name")
}
} else {
if changes.ID != nil {
return fi.CannotChangeField("ID")
}
if changes.Name != nil {
return fi.CannotChangeField("Name")
}
}
return nil
}
func (_ *Instance) ShouldCreate(a, e, changes *Instance) (bool, error) {
return a == nil, nil
}
func (_ *Instance) RenderOpenstack(t *openstack.OpenstackAPITarget, a, e, changes *Instance) error {
if a == nil {
glog.V(2).Infof("Creating Instance with name: %q", fi.StringValue(e.Name))
opt := servers.CreateOpts{
Name: fi.StringValue(e.Name),
ImageName: fi.StringValue(e.Image),
FlavorName: fi.StringValue(e.Flavor),
Networks: []servers.Network{
{
Port: fi.StringValue(e.Port.ID),
},
},
}
keyext := keypairs.CreateOptsExt{
CreateOptsBuilder: opt,
KeyName: fi.StringValue(e.SSHKey),
}
sgext := schedulerhints.CreateOptsExt{
CreateOptsBuilder: keyext,
SchedulerHints: &schedulerhints.SchedulerHints{
Group: fi.StringValue(e.Role),
},
}
v, err := t.Cloud.CreateInstance(sgext)
if err != nil {
return fmt.Errorf("Error creating instance: %v", err)
}
e.ID = fi.String(v.ID)
glog.V(2).Infof("Creating a new Openstack instance, id=%s", v.ID)
return nil
}
glog.V(2).Infof("Openstack task Instance::RenderOpenstack did nothing")
return nil
}

View File

@ -0,0 +1,75 @@
/*
Copyright 2018 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.
*/
// Code generated by ""fitask" -type=Instance"; DO NOT EDIT
package openstacktasks
import (
"encoding/json"
"k8s.io/kops/upup/pkg/fi"
)
// Instance
// JSON marshalling boilerplate
type realInstance Instance
// UnmarshalJSON implements conversion to JSON, supporting an alternate specification of the object as a string
func (o *Instance) UnmarshalJSON(data []byte) error {
var jsonName string
if err := json.Unmarshal(data, &jsonName); err == nil {
o.Name = &jsonName
return nil
}
var r realInstance
if err := json.Unmarshal(data, &r); err != nil {
return err
}
*o = Instance(r)
return nil
}
var _ fi.HasLifecycle = &Instance{}
// GetLifecycle returns the Lifecycle of the object, implementing fi.HasLifecycle
func (o *Instance) GetLifecycle() *fi.Lifecycle {
return o.Lifecycle
}
// SetLifecycle sets the Lifecycle of the object, implementing fi.SetLifecycle
func (o *Instance) SetLifecycle(lifecycle fi.Lifecycle) {
o.Lifecycle = &lifecycle
}
var _ fi.HasName = &Instance{}
// GetName returns the Name of the object, implementing fi.HasName
func (o *Instance) GetName() *string {
return o.Name
}
// SetName sets the Name of the object, implementing fi.SetName
func (o *Instance) SetName(name string) {
o.Name = &name
}
// String is the stringer function for the task, producing readable output using fi.TaskAsString
func (o *Instance) String() string {
return fi.TaskAsString(o)
}

View File

@ -0,0 +1,154 @@
/*
Copyright 2017 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 openstacktasks
import (
"fmt"
"github.com/golang/glog"
"github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners"
"github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers"
"github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools"
"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup/openstack"
)
//go:generate fitask -type=LB
type LB struct {
ID *string
Name *string
Subnet *Subnet
Listeners []listeners.Listener
Lifecycle *fi.Lifecycle
}
var _ fi.CompareWithID = &LB{}
func (s *LB) CompareWithID() *string {
return s.ID
}
func (s *LB) Find(context *fi.Context) (*LB, error) {
if s.ID == nil {
return nil, nil
}
cloud := context.Cloud.(openstack.OpenstackCloud)
lb, err := loadbalancers.Get(cloud.LoadBalancerClient(), fi.StringValue(s.ID)).Extract()
if err != nil {
return nil, err
}
sub, err := subnets.Get(cloud.NetworkingClient(), fi.StringValue(s.Subnet.ID)).Extract()
if err != nil {
return nil, err
}
a := &LB{
ID: fi.String(lb.ID),
Name: fi.String(lb.Name),
Listeners: lb.Listeners,
Lifecycle: s.Lifecycle,
Subnet: &Subnet{
ID: fi.String(sub.ID),
Name: fi.String(sub.Name),
CIDR: fi.String(sub.CIDR),
Network: &Network{
ID: fi.String(sub.NetworkID),
Lifecycle: s.Lifecycle,
},
Lifecycle: s.Lifecycle,
},
}
return a, nil
}
func (s *LB) Run(context *fi.Context) error {
return fi.DefaultDeltaRunMethod(s, context)
}
func (_ *LB) CheckChanges(a, e, changes *LB) error {
if a == nil {
if e.Name == nil {
return fi.RequiredField("Name")
}
} else {
if changes.ID != nil {
return fi.CannotChangeField("ID")
}
if changes.Name != nil {
return fi.CannotChangeField("Name")
}
}
return nil
}
func (_ *LB) RenderOpenstack(t *openstack.OpenstackAPITarget, a, e, changes *LB) error {
if a == nil {
glog.V(2).Infof("Creating LB with Name: %q", fi.StringValue(e.Name))
var vipsubid string
if subid := fi.StringValue(e.Subnet.ID); subid != "" {
vipsubid = subid
} else {
subid, err := subnets.IDFromName(t.Cloud.NetworkingClient(), fi.StringValue(e.Subnet.Name))
if err != nil {
return err
}
vipsubid = subid
}
lbopts := loadbalancers.CreateOpts{
Name: fi.StringValue(e.Name),
VipSubnetID: vipsubid,
}
lb, err := t.Cloud.CreateLB(lbopts)
if err != nil {
return fmt.Errorf("error creating LB: %v", err)
}
e.ID = fi.String(lb.ID)
poolopts := pools.CreateOpts{
Name: lb.Name + "-https",
LBMethod: pools.LBMethodRoundRobin,
Protocol: pools.ProtocolTCP,
LoadbalancerID: lb.ID,
}
pool, err := pools.Create(t.Cloud.LoadBalancerClient(), poolopts).Extract()
if err != nil {
return fmt.Errorf("error creating LB pool: %v", err)
}
listeneropts := listeners.CreateOpts{
Name: lb.Name + "-https",
DefaultPoolID: pool.ID,
LoadbalancerID: lb.ID,
Protocol: listeners.ProtocolTCP,
ProtocolPort: 443,
}
_, err = listeners.Create(t.Cloud.LoadBalancerClient(), listeneropts).Extract()
if err != nil {
return fmt.Errorf("error creating LB listener: %v", err)
}
return nil
}
glog.V(2).Infof("Openstack task LB::RenderOpenstack did nothing")
return nil
}

View File

@ -0,0 +1,75 @@
/*
Copyright 2018 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.
*/
// Code generated by ""fitask" -type=LB"; DO NOT EDIT
package openstacktasks
import (
"encoding/json"
"k8s.io/kops/upup/pkg/fi"
)
// LB
// JSON marshalling boilerplate
type realLB LB
// UnmarshalJSON implements conversion to JSON, supporting an alternate specification of the object as a string
func (o *LB) UnmarshalJSON(data []byte) error {
var jsonName string
if err := json.Unmarshal(data, &jsonName); err == nil {
o.Name = &jsonName
return nil
}
var r realLB
if err := json.Unmarshal(data, &r); err != nil {
return err
}
*o = LB(r)
return nil
}
var _ fi.HasLifecycle = &LB{}
// GetLifecycle returns the Lifecycle of the object, implementing fi.HasLifecycle
func (o *LB) GetLifecycle() *fi.Lifecycle {
return o.Lifecycle
}
// SetLifecycle sets the Lifecycle of the object, implementing fi.SetLifecycle
func (o *LB) SetLifecycle(lifecycle fi.Lifecycle) {
o.Lifecycle = &lifecycle
}
var _ fi.HasName = &LB{}
// GetName returns the Name of the object, implementing fi.HasName
func (o *LB) GetName() *string {
return o.Name
}
// SetName sets the Name of the object, implementing fi.SetName
func (o *LB) SetName(name string) {
o.Name = &name
}
// String is the stringer function for the task, producing readable output using fi.TaskAsString
func (o *LB) String() string {
return fi.TaskAsString(o)
}

View File

@ -0,0 +1,127 @@
/*
Copyright 2018 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 openstacktasks
import (
"fmt"
"github.com/golang/glog"
"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup/openstack"
)
//go:generate fitask -type=Port
type Port struct {
ID *string
Name *string
Network *Network
SecurityGroups []SecurityGroup
Lifecycle *fi.Lifecycle
}
var _ fi.CompareWithID = &Port{}
func (s *Port) CompareWithID() *string {
return s.ID
}
func (s *Port) Find(context *fi.Context) (*Port, error) {
cloud := context.Cloud.(openstack.OpenstackCloud)
opt := ports.ListOpts{
Name: fi.StringValue(s.Name),
}
rs, err := cloud.ListPorts(opt)
if err != nil {
return nil, err
}
if rs == nil {
return nil, nil
} else if len(rs) != 1 {
return nil, fmt.Errorf("found multiple ports with name: %s", fi.StringValue(s.Name))
}
v := rs[0]
sgs := make([]SecurityGroup, len(v.SecurityGroups))
for i, sgid := range v.SecurityGroups {
sgs[i] = SecurityGroup{
ID: fi.String(sgid),
Lifecycle: s.Lifecycle,
}
}
actual := &Port{
ID: fi.String(v.ID),
Name: fi.String(v.Name),
Network: &Network{ID: fi.String(v.NetworkID)},
SecurityGroups: sgs,
Lifecycle: s.Lifecycle,
}
return actual, nil
}
func (s *Port) Run(context *fi.Context) error {
return fi.DefaultDeltaRunMethod(s, context)
}
func (_ *Port) CheckChanges(a, e, changes *Port) error {
if a == nil {
if e.Name == nil {
return fi.RequiredField("Name")
}
if e.Network == nil {
return fi.RequiredField("Network")
}
} else {
if changes.Name != nil {
return fi.CannotChangeField("Name")
}
if e.Network != nil {
return fi.CannotChangeField("Network")
}
}
return nil
}
func (_ *Port) RenderOpenstack(t *openstack.OpenstackAPITarget, a, e, changes *Port) error {
if a == nil {
glog.V(2).Infof("Creating Port with name: %q", fi.StringValue(e.Name))
sgs := make([]string, len(e.SecurityGroups))
for i, sg := range e.SecurityGroups {
sgs[i] = fi.StringValue(sg.ID)
}
opt := ports.CreateOpts{
Name: fi.StringValue(e.Name),
NetworkID: fi.StringValue(e.Network.ID),
SecurityGroups: &sgs,
}
v, err := t.Cloud.CreatePort(opt)
if err != nil {
return fmt.Errorf("Error creating port: %v", err)
}
e.ID = fi.String(v.ID)
glog.V(2).Infof("Creating a new Openstack port, id=%s", v.ID)
return nil
}
e.ID = a.ID
glog.V(2).Infof("Using an existing Openstack port, id=%s", fi.StringValue(e.ID))
return nil
}

View File

@ -0,0 +1,75 @@
/*
Copyright 2018 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.
*/
// Code generated by ""fitask" -type=Port"; DO NOT EDIT
package openstacktasks
import (
"encoding/json"
"k8s.io/kops/upup/pkg/fi"
)
// Port
// JSON marshalling boilerplate
type realPort Port
// UnmarshalJSON implements conversion to JSON, supporting an alternate specification of the object as a string
func (o *Port) UnmarshalJSON(data []byte) error {
var jsonName string
if err := json.Unmarshal(data, &jsonName); err == nil {
o.Name = &jsonName
return nil
}
var r realPort
if err := json.Unmarshal(data, &r); err != nil {
return err
}
*o = Port(r)
return nil
}
var _ fi.HasLifecycle = &Port{}
// GetLifecycle returns the Lifecycle of the object, implementing fi.HasLifecycle
func (o *Port) GetLifecycle() *fi.Lifecycle {
return o.Lifecycle
}
// SetLifecycle sets the Lifecycle of the object, implementing fi.SetLifecycle
func (o *Port) SetLifecycle(lifecycle fi.Lifecycle) {
o.Lifecycle = &lifecycle
}
var _ fi.HasName = &Port{}
// GetName returns the Name of the object, implementing fi.HasName
func (o *Port) GetName() *string {
return o.Name
}
// SetName sets the Name of the object, implementing fi.SetName
func (o *Port) SetName(name string) {
o.Name = &name
}
// String is the stringer function for the task, producing readable output using fi.TaskAsString
func (o *Port) String() string {
return fi.TaskAsString(o)
}

View File

@ -0,0 +1,104 @@
/*
Copyright 2017 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 openstacktasks
import (
"fmt"
"github.com/golang/glog"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup/openstack"
)
//go:generate fitask -type=ServerGroup
type ServerGroup struct {
ID *string
Name *string
Members []string
Policies []string
Lifecycle *fi.Lifecycle
}
var _ fi.CompareWithID = &ServerGroup{}
func (s *ServerGroup) CompareWithID() *string {
return s.ID
}
func (s *ServerGroup) Find(context *fi.Context) (*ServerGroup, error) {
if s == nil || s.ID == nil {
return nil, nil
}
id := *(s.ID)
cloud := context.Cloud.(openstack.OpenstackCloud)
g, err := servergroups.Get(cloud.ComputeClient(), id).Extract()
if err != nil {
return nil, err
}
a := &ServerGroup{
ID: fi.String(g.ID),
Name: fi.String(g.Name),
Members: g.Members,
Policies: g.Policies,
Lifecycle: s.Lifecycle,
}
return a, nil
}
func (s *ServerGroup) Run(context *fi.Context) error {
return fi.DefaultDeltaRunMethod(s, context)
}
func (_ *ServerGroup) CheckChanges(a, e, changes *ServerGroup) error {
if a == nil {
if e.Name == nil {
return fi.RequiredField("Name")
}
} else {
if changes.ID != nil {
return fi.CannotChangeField("ID")
}
if changes.Name != nil {
return fi.CannotChangeField("Name")
}
}
return nil
}
func (_ *ServerGroup) RenderOpenstack(t *openstack.OpenstackAPITarget, a, e, changes *ServerGroup) error {
if a == nil {
glog.V(2).Infof("Creating ServerGroup with Name:%q", fi.StringValue(e.Name))
opt := servergroups.CreateOpts{
Name: fi.StringValue(e.Name),
Policies: e.Policies,
}
g, err := t.Cloud.CreateServerGroup(opt)
if err != nil {
return fmt.Errorf("error creating ServerGroup: %v", err)
}
e.ID = fi.String(g.ID)
return nil
}
glog.V(2).Infof("Openstack task ServerGroup::RenderOpenstack did nothing")
return nil
}

View File

@ -0,0 +1,75 @@
/*
Copyright 2018 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.
*/
// Code generated by ""fitask" -type=ServerGroup"; DO NOT EDIT
package openstacktasks
import (
"encoding/json"
"k8s.io/kops/upup/pkg/fi"
)
// ServerGroup
// JSON marshalling boilerplate
type realServerGroup ServerGroup
// UnmarshalJSON implements conversion to JSON, supporting an alternate specification of the object as a string
func (o *ServerGroup) UnmarshalJSON(data []byte) error {
var jsonName string
if err := json.Unmarshal(data, &jsonName); err == nil {
o.Name = &jsonName
return nil
}
var r realServerGroup
if err := json.Unmarshal(data, &r); err != nil {
return err
}
*o = ServerGroup(r)
return nil
}
var _ fi.HasLifecycle = &ServerGroup{}
// GetLifecycle returns the Lifecycle of the object, implementing fi.HasLifecycle
func (o *ServerGroup) GetLifecycle() *fi.Lifecycle {
return o.Lifecycle
}
// SetLifecycle sets the Lifecycle of the object, implementing fi.SetLifecycle
func (o *ServerGroup) SetLifecycle(lifecycle fi.Lifecycle) {
o.Lifecycle = &lifecycle
}
var _ fi.HasName = &ServerGroup{}
// GetName returns the Name of the object, implementing fi.HasName
func (o *ServerGroup) GetName() *string {
return o.Name
}
// SetName sets the Name of the object, implementing fi.SetName
func (o *ServerGroup) SetName(name string) {
o.Name = &name
}
// String is the stringer function for the task, producing readable output using fi.TaskAsString
func (o *ServerGroup) String() string {
return fi.TaskAsString(o)
}

View File

@ -20,7 +20,7 @@ import (
"fmt"
"github.com/golang/glog"
cinder "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes"
cinderv2 "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup/openstack"
)
@ -44,9 +44,9 @@ func (c *Volume) CompareWithID() *string {
func (c *Volume) Find(context *fi.Context) (*Volume, error) {
cloud := context.Cloud.(openstack.OpenstackCloud)
opt := cinder.ListOpts{
opt := cinderv2.ListOpts{
Name: fi.StringValue(c.Name),
Metadata: cloud.GetCloudTags(),
Metadata: c.Tags,
}
volumes, err := cloud.ListVolumes(opt)
if err != nil {
@ -115,7 +115,7 @@ func (_ *Volume) RenderOpenstack(t *openstack.OpenstackAPITarget, a, e, changes
if a == nil {
glog.V(2).Infof("Creating PersistentVolume with Name:%q", fi.StringValue(e.Name))
opt := cinder.CreateOpts{
opt := cinderv2.CreateOpts{
Size: int(*e.SizeGB),
AvailabilityZone: fi.StringValue(e.AvailabilityZone),
Metadata: e.Tags,

View File

@ -18,9 +18,11 @@ package vfs
import (
"bytes"
"crypto/tls"
"encoding/hex"
"fmt"
"io"
"net/http"
"os"
"path"
"path/filepath"
@ -47,16 +49,32 @@ func NewSwiftClient() (*gophercloud.ServiceClient, error) {
if err != nil {
return nil, err
}
provider, err := openstack.AuthenticatedClient(authOption)
pc, err := openstack.NewClient(authOption.IdentityEndpoint)
if err != nil {
return nil, fmt.Errorf("error building openstack provider client: %v", err)
}
tlsconfig := &tls.Config{}
tlsconfig.InsecureSkipVerify = true
transport := &http.Transport{TLSClientConfig: tlsconfig}
pc.HTTPClient = http.Client{
Transport: transport,
}
glog.V(2).Info("authenticating to keystone")
err = openstack.Authenticate(pc, authOption)
if err != nil {
return nil, fmt.Errorf("error building openstack authenticated client: %v", err)
}
endpointOpt, err := config.GetServiceConfig("Swift")
if err != nil {
return nil, err
}
client, err := openstack.NewObjectStorageV1(provider, endpointOpt)
client, err := openstack.NewObjectStorageV1(pc, endpointOpt)
if err != nil {
return nil, fmt.Errorf("error building swift client: %v", err)
}