diff --git a/Makefile b/Makefile index 20dcc071c7..e7e3778736 100644 --- a/Makefile +++ b/Makefile @@ -909,4 +909,4 @@ crds: .PHONY: kops-controller-push kops-controller-push: - DOCKER_REGISTRY=${DOCKER_REGISTRY} DOCKER_IMAGE_PREFIX=${DOCKER_IMAGE_PREFIX} KOPS_CONTROLLER_TAG=${KOPS_CONTROLLER_TAG} bazel run //cmd/kops-controller:push-image + DOCKER_REGISTRY=${DOCKER_REGISTRY} DOCKER_IMAGE_PREFIX=${DOCKER_IMAGE_PREFIX} KOPS_CONTROLLER_TAG=${KOPS_CONTROLLER_TAG} bazel run --platforms=@io_bazel_rules_go//go/toolchain:linux_amd64 //cmd/kops-controller:push-image diff --git a/cmd/kops-controller/BUILD.bazel b/cmd/kops-controller/BUILD.bazel index 19e0c19897..80bab6eb33 100644 --- a/cmd/kops-controller/BUILD.bazel +++ b/cmd/kops-controller/BUILD.bazel @@ -10,6 +10,7 @@ go_library( "//pkg/nodeidentity:go_default_library", "//pkg/nodeidentity/aws:go_default_library", "//pkg/nodeidentity/gce:go_default_library", + "//pkg/nodeidentity/openstack:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/client-go/plugin/pkg/client/auth/gcp:go_default_library", diff --git a/cmd/kops-controller/main.go b/cmd/kops-controller/main.go index 870a9a2fc7..c00526b614 100644 --- a/cmd/kops-controller/main.go +++ b/cmd/kops-controller/main.go @@ -33,6 +33,8 @@ import ( "k8s.io/kops/pkg/nodeidentity" nodeidentityaws "k8s.io/kops/pkg/nodeidentity/aws" nodeidentitygce "k8s.io/kops/pkg/nodeidentity/gce" + nodeidentityos "k8s.io/kops/pkg/nodeidentity/openstack" + "sigs.k8s.io/controller-runtime/pkg/manager" ) @@ -118,6 +120,12 @@ func addNodeController(mgr manager.Manager, opt *Options) error { return fmt.Errorf("error building identifier: %v", err) } + case "openstack": + identifier, err = nodeidentityos.New() + if err != nil { + return fmt.Errorf("error building identifier: %v", err) + } + case "": return fmt.Errorf("must specify cloud") diff --git a/hack/.packages b/hack/.packages index 2b1a50b6a6..bc2acfe245 100644 --- a/hack/.packages +++ b/hack/.packages @@ -112,6 +112,7 @@ k8s.io/kops/pkg/model/vspheremodel k8s.io/kops/pkg/nodeidentity k8s.io/kops/pkg/nodeidentity/aws k8s.io/kops/pkg/nodeidentity/gce +k8s.io/kops/pkg/nodeidentity/openstack k8s.io/kops/pkg/nodelabels k8s.io/kops/pkg/pki k8s.io/kops/pkg/pkiutil diff --git a/pkg/nodeidentity/openstack/BUILD.bazel b/pkg/nodeidentity/openstack/BUILD.bazel new file mode 100644 index 0000000000..0df07279ee --- /dev/null +++ b/pkg/nodeidentity/openstack/BUILD.bazel @@ -0,0 +1,15 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["identify.go"], + importpath = "k8s.io/kops/pkg/nodeidentity/openstack", + visibility = ["//visibility:public"], + deps = [ + "//pkg/nodeidentity:go_default_library", + "//vendor/github.com/gophercloud/gophercloud:go_default_library", + "//vendor/github.com/gophercloud/gophercloud/openstack:go_default_library", + "//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers:go_default_library", + "//vendor/k8s.io/api/core/v1:go_default_library", + ], +) diff --git a/pkg/nodeidentity/openstack/identify.go b/pkg/nodeidentity/openstack/identify.go new file mode 100644 index 0000000000..47716a16de --- /dev/null +++ b/pkg/nodeidentity/openstack/identify.go @@ -0,0 +1,110 @@ +/* +Copyright 2019 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 ( + "context" + "fmt" + "os" + "strings" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack" + "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" + corev1 "k8s.io/api/core/v1" + "k8s.io/kops/pkg/nodeidentity" +) + +// nodeIdentifier identifies a node +type nodeIdentifier struct { + novaClient *gophercloud.ServiceClient +} + +// New creates and returns a nodeidentity.Identifier for Nodes running on OpenStack +func New() (nodeidentity.Identifier, error) { + env, err := openstack.AuthOptionsFromEnv() + if err != nil { + return nil, err + } + + region := os.Getenv("OS_REGION_NAME") + if region == "" { + return nil, fmt.Errorf("Unable to find region") + } + + provider, err := openstack.NewClient(env.IdentityEndpoint) + if err != nil { + return nil, err + } + + err = openstack.Authenticate(provider, env) + if err != nil { + return nil, err + } + + novaClient, err := openstack.NewComputeV2(provider, gophercloud.EndpointOpts{ + Type: "compute", + Region: region, + }) + if err != nil { + return nil, fmt.Errorf("error building nova client: %v", err) + } + + return &nodeIdentifier{ + novaClient: novaClient, + }, nil +} + +// IdentifyNode queries OpenStack for the node identity information +func (i *nodeIdentifier) IdentifyNode(ctx context.Context, node *corev1.Node) (*nodeidentity.Info, error) { + providerID := node.Spec.ProviderID + if providerID == "" { + return nil, fmt.Errorf("providerID was not set for node %s", node.Name) + } + if !strings.HasPrefix(providerID, "openstack://") { + return nil, fmt.Errorf("providerID %q not recognized for node %s", providerID, node.Name) + } + + instanceID := strings.TrimPrefix(providerID, "openstack://") + // instanceid looks like its openstack:/// but no idea is that really correct like that? + // this supports now both openstack:// and openstack:/// format + if strings.HasPrefix(instanceID, "/") { + instanceID = strings.TrimPrefix(instanceID, "/") + } + + kopsGroup, err := i.getInstanceGroup(instanceID) + if err != nil { + return nil, err + } + + info := &nodeidentity.Info{} + info.InstanceGroup = kopsGroup + + return info, nil +} + +func (i *nodeIdentifier) getInstanceGroup(instanceID string) (string, error) { + instance, err := servers.Get(i.novaClient, instanceID).Extract() + if err != nil { + return "", err + } + + if val, ok := instance.Metadata["KopsInstanceGroup"]; ok { + return val, nil + } + return "", fmt.Errorf("Could not find tag 'KopsInstanceGroup' from instance metadata") +}