diff --git a/cmd/kube-controller-manager/app/BUILD b/cmd/kube-controller-manager/app/BUILD index 6fdd483cedb..1158d2ed4ac 100644 --- a/cmd/kube-controller-manager/app/BUILD +++ b/cmd/kube-controller-manager/app/BUILD @@ -100,7 +100,6 @@ go_library( "//pkg/volume/iscsi:go_default_library", "//pkg/volume/local:go_default_library", "//pkg/volume/nfs:go_default_library", - "//pkg/volume/photon_pd:go_default_library", "//pkg/volume/portworx:go_default_library", "//pkg/volume/quobyte:go_default_library", "//pkg/volume/rbd:go_default_library", diff --git a/cmd/kube-controller-manager/app/plugins.go b/cmd/kube-controller-manager/app/plugins.go index 514c8fa94d7..bb587407990 100644 --- a/cmd/kube-controller-manager/app/plugins.go +++ b/cmd/kube-controller-manager/app/plugins.go @@ -45,7 +45,6 @@ import ( "k8s.io/kubernetes/pkg/volume/iscsi" "k8s.io/kubernetes/pkg/volume/local" "k8s.io/kubernetes/pkg/volume/nfs" - "k8s.io/kubernetes/pkg/volume/photon_pd" "k8s.io/kubernetes/pkg/volume/portworx" "k8s.io/kubernetes/pkg/volume/quobyte" "k8s.io/kubernetes/pkg/volume/rbd" @@ -73,7 +72,6 @@ func ProbeAttachableVolumePlugins() []volume.VolumePlugin { allPlugins = append(allPlugins, portworx.ProbeVolumePlugins()...) allPlugins = append(allPlugins, vsphere_volume.ProbeVolumePlugins()...) allPlugins = append(allPlugins, azure_dd.ProbeVolumePlugins()...) - allPlugins = append(allPlugins, photon_pd.ProbeVolumePlugins()...) allPlugins = append(allPlugins, scaleio.ProbeVolumePlugins()...) allPlugins = append(allPlugins, storageos.ProbeVolumePlugins()...) allPlugins = append(allPlugins, fc.ProbeVolumePlugins()...) @@ -105,7 +103,6 @@ func ProbeExpandableVolumePlugins(config persistentvolumeconfig.VolumeConfigurat allPlugins = append(allPlugins, rbd.ProbeVolumePlugins()...) allPlugins = append(allPlugins, azure_dd.ProbeVolumePlugins()...) allPlugins = append(allPlugins, azure_file.ProbeVolumePlugins()...) - allPlugins = append(allPlugins, photon_pd.ProbeVolumePlugins()...) allPlugins = append(allPlugins, scaleio.ProbeVolumePlugins()...) allPlugins = append(allPlugins, storageos.ProbeVolumePlugins()...) allPlugins = append(allPlugins, fc.ProbeVolumePlugins()...) @@ -164,7 +161,6 @@ func ProbeControllerVolumePlugins(cloud cloudprovider.Interface, config persiste allPlugins = append(allPlugins, cinder.ProbeVolumePlugins()...) allPlugins = append(allPlugins, vsphere_volume.ProbeVolumePlugins()...) allPlugins = append(allPlugins, azure_dd.ProbeVolumePlugins()...) - allPlugins = append(allPlugins, photon_pd.ProbeVolumePlugins()...) if utilfeature.DefaultFeatureGate.Enabled(features.CSIInlineVolume) { allPlugins = append(allPlugins, csi.ProbeVolumePlugins()...) diff --git a/cmd/kubeadm/.import-restrictions b/cmd/kubeadm/.import-restrictions index 510a28f8525..a8e7f8f77c9 100644 --- a/cmd/kubeadm/.import-restrictions +++ b/cmd/kubeadm/.import-restrictions @@ -95,12 +95,9 @@ "k8s.io/kubernetes/pkg/cloudprovider/providers", "k8s.io/kubernetes/pkg/cloudprovider/providers/aws", "k8s.io/kubernetes/pkg/cloudprovider/providers/azure", - "k8s.io/kubernetes/pkg/cloudprovider/providers/cloudstack", "k8s.io/kubernetes/pkg/cloudprovider/providers/fake", "k8s.io/kubernetes/pkg/cloudprovider/providers/gce", "k8s.io/kubernetes/pkg/cloudprovider/providers/openstack", - "k8s.io/kubernetes/pkg/cloudprovider/providers/ovirt", - "k8s.io/kubernetes/pkg/cloudprovider/providers/photon", "k8s.io/kubernetes/pkg/cloudprovider/providers/vsphere", "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" ] diff --git a/cmd/kubelet/app/BUILD b/cmd/kubelet/app/BUILD index d26be77596e..01b645bd52e 100644 --- a/cmd/kubelet/app/BUILD +++ b/cmd/kubelet/app/BUILD @@ -99,7 +99,6 @@ go_library( "//pkg/volume/iscsi:go_default_library", "//pkg/volume/local:go_default_library", "//pkg/volume/nfs:go_default_library", - "//pkg/volume/photon_pd:go_default_library", "//pkg/volume/portworx:go_default_library", "//pkg/volume/projected:go_default_library", "//pkg/volume/quobyte:go_default_library", diff --git a/cmd/kubelet/app/plugins.go b/cmd/kubelet/app/plugins.go index c566016a270..7a0a7321211 100644 --- a/cmd/kubelet/app/plugins.go +++ b/cmd/kubelet/app/plugins.go @@ -44,7 +44,6 @@ import ( "k8s.io/kubernetes/pkg/volume/iscsi" "k8s.io/kubernetes/pkg/volume/local" "k8s.io/kubernetes/pkg/volume/nfs" - "k8s.io/kubernetes/pkg/volume/photon_pd" "k8s.io/kubernetes/pkg/volume/portworx" "k8s.io/kubernetes/pkg/volume/projected" "k8s.io/kubernetes/pkg/volume/quobyte" @@ -90,7 +89,6 @@ func ProbeVolumePlugins() []volume.VolumePlugin { allPlugins = append(allPlugins, configmap.ProbeVolumePlugins()...) allPlugins = append(allPlugins, vsphere_volume.ProbeVolumePlugins()...) allPlugins = append(allPlugins, azure_dd.ProbeVolumePlugins()...) - allPlugins = append(allPlugins, photon_pd.ProbeVolumePlugins()...) allPlugins = append(allPlugins, projected.ProbeVolumePlugins()...) allPlugins = append(allPlugins, portworx.ProbeVolumePlugins()...) allPlugins = append(allPlugins, scaleio.ProbeVolumePlugins()...) diff --git a/hack/.golint_failures b/hack/.golint_failures index 795f37bf204..b4734a30b62 100644 --- a/hack/.golint_failures +++ b/hack/.golint_failures @@ -51,7 +51,6 @@ pkg/apis/scheduling/v1beta1 pkg/apis/storage pkg/apis/storage/v1 pkg/apis/storage/v1beta1 -pkg/cloudprovider/providers/photon pkg/controller pkg/controller/apis/config/v1alpha1 pkg/controller/certificates @@ -315,7 +314,6 @@ pkg/volume/csi/csiv0 pkg/volume/csi/fake pkg/volume/git_repo pkg/volume/iscsi -pkg/volume/photon_pd pkg/volume/rbd pkg/volume/scaleio pkg/volume/storageos diff --git a/pkg/cloudprovider/providers/BUILD b/pkg/cloudprovider/providers/BUILD index 66dc0a30f05..5bccc7c4787 100644 --- a/pkg/cloudprovider/providers/BUILD +++ b/pkg/cloudprovider/providers/BUILD @@ -13,10 +13,7 @@ go_library( "//cmd/kubelet/app:__pkg__", ], deps = [ - "//pkg/cloudprovider/providers/cloudstack:go_default_library", "//pkg/cloudprovider/providers/openstack:go_default_library", - "//pkg/cloudprovider/providers/ovirt:go_default_library", - "//pkg/cloudprovider/providers/photon:go_default_library", "//staging/src/k8s.io/legacy-cloud-providers/aws:go_default_library", "//staging/src/k8s.io/legacy-cloud-providers/azure:go_default_library", "//staging/src/k8s.io/legacy-cloud-providers/gce:go_default_library", @@ -35,10 +32,7 @@ filegroup( name = "all-srcs", srcs = [ ":package-srcs", - "//pkg/cloudprovider/providers/cloudstack:all-srcs", "//pkg/cloudprovider/providers/openstack:all-srcs", - "//pkg/cloudprovider/providers/ovirt:all-srcs", - "//pkg/cloudprovider/providers/photon:all-srcs", ], tags = ["automanaged"], ) diff --git a/pkg/cloudprovider/providers/cloudstack/BUILD b/pkg/cloudprovider/providers/cloudstack/BUILD deleted file mode 100644 index 65cd15f2572..00000000000 --- a/pkg/cloudprovider/providers/cloudstack/BUILD +++ /dev/null @@ -1,88 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", - "go_test", -) - -go_library( - name = "go_default_library", - srcs = [ - "cloudstack.go", - "cloudstack_instances.go", - "cloudstack_loadbalancer.go", - "metadata.go", - "metadata_linux.go", - "metadata_other.go", - ], - importpath = "k8s.io/kubernetes/pkg/cloudprovider/providers/cloudstack", - deps = [ - "//staging/src/k8s.io/api/core/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", - "//staging/src/k8s.io/cloud-provider:go_default_library", - "//vendor/github.com/d2g/dhcp4:go_default_library", - "//vendor/github.com/kardianos/osext:go_default_library", - "//vendor/github.com/xanzy/go-cloudstack/cloudstack:go_default_library", - "//vendor/gopkg.in/gcfg.v1:go_default_library", - "//vendor/k8s.io/klog:go_default_library", - ] + select({ - "@io_bazel_rules_go//go/platform:android": [ - "//vendor/github.com/d2g/dhcp4client:go_default_library", - ], - "@io_bazel_rules_go//go/platform:darwin": [ - "//vendor/github.com/d2g/dhcp4client:go_default_library", - ], - "@io_bazel_rules_go//go/platform:dragonfly": [ - "//vendor/github.com/d2g/dhcp4client:go_default_library", - ], - "@io_bazel_rules_go//go/platform:freebsd": [ - "//vendor/github.com/d2g/dhcp4client:go_default_library", - ], - "@io_bazel_rules_go//go/platform:linux": [ - "//vendor/github.com/d2g/dhcp4client:go_default_library", - ], - "@io_bazel_rules_go//go/platform:nacl": [ - "//vendor/github.com/d2g/dhcp4client:go_default_library", - ], - "@io_bazel_rules_go//go/platform:netbsd": [ - "//vendor/github.com/d2g/dhcp4client:go_default_library", - ], - "@io_bazel_rules_go//go/platform:openbsd": [ - "//vendor/github.com/d2g/dhcp4client:go_default_library", - ], - "@io_bazel_rules_go//go/platform:plan9": [ - "//vendor/github.com/d2g/dhcp4client:go_default_library", - ], - "@io_bazel_rules_go//go/platform:solaris": [ - "//vendor/github.com/d2g/dhcp4client:go_default_library", - ], - "@io_bazel_rules_go//go/platform:windows": [ - "//vendor/github.com/d2g/dhcp4client:go_default_library", - ], - "//conditions:default": [], - }), -) - -go_test( - name = "go_default_test", - srcs = ["cloudstack_test.go"], - embed = [":go_default_library"], - deps = [ - "//staging/src/k8s.io/api/core/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - ], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [":package-srcs"], - tags = ["automanaged"], -) diff --git a/pkg/cloudprovider/providers/cloudstack/OWNERS b/pkg/cloudprovider/providers/cloudstack/OWNERS deleted file mode 100644 index 111e3dd19d5..00000000000 --- a/pkg/cloudprovider/providers/cloudstack/OWNERS +++ /dev/null @@ -1,6 +0,0 @@ -# See the OWNERS docs at https://go.k8s.io/owners - -approvers: -- ngtuna -- sebgoa -- svanharmelen diff --git a/pkg/cloudprovider/providers/cloudstack/cloudstack.go b/pkg/cloudprovider/providers/cloudstack/cloudstack.go deleted file mode 100644 index 222a0356293..00000000000 --- a/pkg/cloudprovider/providers/cloudstack/cloudstack.go +++ /dev/null @@ -1,265 +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 cloudstack - -import ( - "context" - "errors" - "fmt" - "io" - "os" - "path/filepath" - - "github.com/kardianos/osext" - "github.com/xanzy/go-cloudstack/cloudstack" - "gopkg.in/gcfg.v1" - "k8s.io/apimachinery/pkg/types" - cloudprovider "k8s.io/cloud-provider" - "k8s.io/klog" -) - -// ProviderName is the name of this cloud provider. -const ProviderName = "cloudstack" - -// CSConfig wraps the config for the CloudStack cloud provider. -type CSConfig struct { - Global struct { - APIURL string `gcfg:"api-url"` - APIKey string `gcfg:"api-key"` - SecretKey string `gcfg:"secret-key"` - SSLNoVerify bool `gcfg:"ssl-no-verify"` - ProjectID string `gcfg:"project-id"` - Zone string `gcfg:"zone"` - } -} - -// CSCloud is an implementation of Interface for CloudStack. -type CSCloud struct { - client *cloudstack.CloudStackClient - metadata *metadata - projectID string // If non-"", all resources will be created within this project - zone string -} - -func init() { - cloudprovider.RegisterCloudProvider(ProviderName, func(config io.Reader) (cloudprovider.Interface, error) { - cfg, err := readConfig(config) - if err != nil { - return nil, err - } - - return newCSCloud(cfg) - }) -} - -func readConfig(config io.Reader) (*CSConfig, error) { - cfg := &CSConfig{} - - if config == nil { - return cfg, nil - } - - if err := gcfg.ReadInto(cfg, config); err != nil { - return nil, fmt.Errorf("could not parse cloud provider config: %v", err) - } - - return cfg, nil -} - -// newCSCloud creates a new instance of CSCloud. -func newCSCloud(cfg *CSConfig) (*CSCloud, error) { - cs := &CSCloud{ - projectID: cfg.Global.ProjectID, - zone: cfg.Global.Zone, - } - - exe, err := osext.Executable() - if err != nil { - return nil, fmt.Errorf("cloud not find the service executable: %v", err) - } - - // When running the kubelet service it's fine to not specify a config file (or only a - // partial config file) as all needed info can be retrieved anonymously using metadata. - if filepath.Base(exe) == "kubelet" || filepath.Base(exe) == "kubelet.exe" { - // In CloudStack your metadata is always served by the DHCP server. - dhcpServer, err := findDHCPServer() - if err == nil { - klog.V(4).Infof("Found metadata server: %v", dhcpServer) - cs.metadata = &metadata{dhcpServer: dhcpServer, zone: cs.zone} - } else { - klog.Errorf("Error searching metadata server: %v", err) - } - } - - if cfg.Global.APIURL != "" && cfg.Global.APIKey != "" && cfg.Global.SecretKey != "" { - cs.client = cloudstack.NewAsyncClient(cfg.Global.APIURL, cfg.Global.APIKey, cfg.Global.SecretKey, !cfg.Global.SSLNoVerify) - } - - if cs.client == nil { - if cs.metadata != nil { - klog.V(2).Infof("No API URL, key and secret are provided, so only using metadata!") - } else { - return nil, errors.New("no cloud provider config given") - } - } - - return cs, nil -} - -var _ cloudprovider.Interface = (*CSCloud)(nil) -var _ cloudprovider.Instances = (*CSCloud)(nil) -var _ cloudprovider.LoadBalancer = (*CSCloud)(nil) -var _ cloudprovider.Zones = (*CSCloud)(nil) - -// Initialize passes a Kubernetes clientBuilder interface to the cloud provider -func (cs *CSCloud) Initialize(clientBuilder cloudprovider.ControllerClientBuilder, stop <-chan struct{}) { -} - -// LoadBalancer returns an implementation of LoadBalancer for CloudStack. -func (cs *CSCloud) LoadBalancer() (cloudprovider.LoadBalancer, bool) { - if cs.client == nil { - return nil, false - } - - return cs, true -} - -// Instances returns an implementation of Instances for CloudStack. -func (cs *CSCloud) Instances() (cloudprovider.Instances, bool) { - if cs.metadata != nil { - return cs.metadata, true - } - - if cs.client == nil { - return nil, false - } - - return cs, true -} - -// Zones returns an implementation of Zones for CloudStack. -func (cs *CSCloud) Zones() (cloudprovider.Zones, bool) { - if cs.metadata != nil { - return cs.metadata, true - } - - if cs.client == nil { - return nil, false - } - - return cs, true -} - -// Clusters returns an implementation of Clusters for CloudStack. -func (cs *CSCloud) Clusters() (cloudprovider.Clusters, bool) { - if cs.client == nil { - return nil, false - } - - return nil, false -} - -// Routes returns an implementation of Routes for CloudStack. -func (cs *CSCloud) Routes() (cloudprovider.Routes, bool) { - if cs.client == nil { - return nil, false - } - - return nil, false -} - -// ProviderName returns the cloud provider ID. -func (cs *CSCloud) ProviderName() string { - return ProviderName -} - -// HasClusterID returns true if the cluster has a clusterID -func (cs *CSCloud) HasClusterID() bool { - return true -} - -// GetZone returns the Zone containing the region that the program is running in. -func (cs *CSCloud) GetZone(ctx context.Context) (cloudprovider.Zone, error) { - zone := cloudprovider.Zone{} - - if cs.zone == "" { - hostname, err := os.Hostname() - if err != nil { - return zone, fmt.Errorf("failed to get hostname for retrieving the zone: %v", err) - } - - instance, count, err := cs.client.VirtualMachine.GetVirtualMachineByName(hostname) - if err != nil { - if count == 0 { - return zone, fmt.Errorf("could not find instance for retrieving the zone: %v", err) - } - return zone, fmt.Errorf("error getting instance for retrieving the zone: %v", err) - } - - cs.zone = instance.Zonename - } - - klog.V(2).Infof("Current zone is %v", cs.zone) - zone.FailureDomain = cs.zone - zone.Region = cs.zone - - return zone, nil -} - -// GetZoneByProviderID returns the Zone, found by using the provider ID. -func (cs *CSCloud) GetZoneByProviderID(ctx context.Context, providerID string) (cloudprovider.Zone, error) { - zone := cloudprovider.Zone{} - - instance, count, err := cs.client.VirtualMachine.GetVirtualMachineByID( - providerID, - cloudstack.WithProject(cs.projectID), - ) - if err != nil { - if count == 0 { - return zone, fmt.Errorf("could not find node by ID: %v", providerID) - } - return zone, fmt.Errorf("error retrieving zone: %v", err) - } - - klog.V(2).Infof("Current zone is %v", cs.zone) - zone.FailureDomain = instance.Zonename - zone.Region = instance.Zonename - - return zone, nil -} - -// GetZoneByNodeName returns the Zone, found by using the node name. -func (cs *CSCloud) GetZoneByNodeName(ctx context.Context, nodeName types.NodeName) (cloudprovider.Zone, error) { - zone := cloudprovider.Zone{} - - instance, count, err := cs.client.VirtualMachine.GetVirtualMachineByName( - string(nodeName), - cloudstack.WithProject(cs.projectID), - ) - if err != nil { - if count == 0 { - return zone, fmt.Errorf("could not find node: %v", nodeName) - } - return zone, fmt.Errorf("error retrieving zone: %v", err) - } - - klog.V(2).Infof("Current zone is %v", cs.zone) - zone.FailureDomain = instance.Zonename - zone.Region = instance.Zonename - - return zone, nil -} diff --git a/pkg/cloudprovider/providers/cloudstack/cloudstack_instances.go b/pkg/cloudprovider/providers/cloudstack/cloudstack_instances.go deleted file mode 100644 index 55a81bdaa6f..00000000000 --- a/pkg/cloudprovider/providers/cloudstack/cloudstack_instances.go +++ /dev/null @@ -1,164 +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 cloudstack - -import ( - "context" - "errors" - "fmt" - - "github.com/xanzy/go-cloudstack/cloudstack" - "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" - cloudprovider "k8s.io/cloud-provider" - "k8s.io/klog" -) - -// NodeAddresses returns the addresses of the specified instance. -func (cs *CSCloud) NodeAddresses(ctx context.Context, name types.NodeName) ([]v1.NodeAddress, error) { - instance, count, err := cs.client.VirtualMachine.GetVirtualMachineByName( - string(name), - cloudstack.WithProject(cs.projectID), - ) - if err != nil { - if count == 0 { - return nil, cloudprovider.InstanceNotFound - } - return nil, fmt.Errorf("error retrieving node addresses: %v", err) - } - - return cs.nodeAddresses(instance) -} - -// NodeAddressesByProviderID returns the addresses of the specified instance. -func (cs *CSCloud) NodeAddressesByProviderID(ctx context.Context, providerID string) ([]v1.NodeAddress, error) { - instance, count, err := cs.client.VirtualMachine.GetVirtualMachineByID( - providerID, - cloudstack.WithProject(cs.projectID), - ) - if err != nil { - if count == 0 { - return nil, cloudprovider.InstanceNotFound - } - return nil, fmt.Errorf("error retrieving node addresses: %v", err) - } - - return cs.nodeAddresses(instance) -} - -func (cs *CSCloud) nodeAddresses(instance *cloudstack.VirtualMachine) ([]v1.NodeAddress, error) { - if len(instance.Nic) == 0 { - return nil, errors.New("instance does not have an internal IP") - } - - addresses := []v1.NodeAddress{ - {Type: v1.NodeInternalIP, Address: instance.Nic[0].Ipaddress}, - } - - if instance.Hostname != "" { - addresses = append(addresses, v1.NodeAddress{Type: v1.NodeHostName, Address: instance.Hostname}) - } - - if instance.Publicip != "" { - addresses = append(addresses, v1.NodeAddress{Type: v1.NodeExternalIP, Address: instance.Publicip}) - } else { - // Since there is no sane way to determine the external IP if the host isn't - // using static NAT, we will just fire a log message and omit the external IP. - klog.V(4).Infof("Could not determine the public IP of host %v (%v)", instance.Name, instance.Id) - } - - return addresses, nil -} - -// InstanceID returns the cloud provider ID of the specified instance. -func (cs *CSCloud) InstanceID(ctx context.Context, name types.NodeName) (string, error) { - instance, count, err := cs.client.VirtualMachine.GetVirtualMachineByName( - string(name), - cloudstack.WithProject(cs.projectID), - ) - if err != nil { - if count == 0 { - return "", cloudprovider.InstanceNotFound - } - return "", fmt.Errorf("error retrieving instance ID: %v", err) - } - - return instance.Id, nil -} - -// InstanceType returns the type of the specified instance. -func (cs *CSCloud) InstanceType(ctx context.Context, name types.NodeName) (string, error) { - instance, count, err := cs.client.VirtualMachine.GetVirtualMachineByName( - string(name), - cloudstack.WithProject(cs.projectID), - ) - if err != nil { - if count == 0 { - return "", cloudprovider.InstanceNotFound - } - return "", fmt.Errorf("error retrieving instance type: %v", err) - } - - return instance.Serviceofferingname, nil -} - -// InstanceTypeByProviderID returns the type of the specified instance. -func (cs *CSCloud) InstanceTypeByProviderID(ctx context.Context, providerID string) (string, error) { - instance, count, err := cs.client.VirtualMachine.GetVirtualMachineByID( - providerID, - cloudstack.WithProject(cs.projectID), - ) - if err != nil { - if count == 0 { - return "", cloudprovider.InstanceNotFound - } - return "", fmt.Errorf("error retrieving instance type: %v", err) - } - - return instance.Serviceofferingname, nil -} - -// AddSSHKeyToAllInstances is currently not implemented. -func (cs *CSCloud) AddSSHKeyToAllInstances(ctx context.Context, user string, keyData []byte) error { - return cloudprovider.NotImplemented -} - -// CurrentNodeName returns the name of the node we are currently running on. -func (cs *CSCloud) CurrentNodeName(ctx context.Context, hostname string) (types.NodeName, error) { - return types.NodeName(hostname), nil -} - -// InstanceExistsByProviderID returns if the instance still exists. -func (cs *CSCloud) InstanceExistsByProviderID(ctx context.Context, providerID string) (bool, error) { - _, count, err := cs.client.VirtualMachine.GetVirtualMachineByID( - providerID, - cloudstack.WithProject(cs.projectID), - ) - if err != nil { - if count == 0 { - return false, nil - } - return false, fmt.Errorf("error retrieving instance: %v", err) - } - - return true, nil -} - -// InstanceShutdownByProviderID returns true if the instance is in safe state to detach volumes -func (cs *CSCloud) InstanceShutdownByProviderID(ctx context.Context, providerID string) (bool, error) { - return false, cloudprovider.NotImplemented -} diff --git a/pkg/cloudprovider/providers/cloudstack/cloudstack_loadbalancer.go b/pkg/cloudprovider/providers/cloudstack/cloudstack_loadbalancer.go deleted file mode 100644 index 64971d02cf2..00000000000 --- a/pkg/cloudprovider/providers/cloudstack/cloudstack_loadbalancer.go +++ /dev/null @@ -1,549 +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 cloudstack - -import ( - "context" - "fmt" - "strconv" - - "github.com/xanzy/go-cloudstack/cloudstack" - "k8s.io/klog" - - "k8s.io/api/core/v1" - cloudprovider "k8s.io/cloud-provider" -) - -type loadBalancer struct { - *cloudstack.CloudStackClient - - name string - algorithm string - hostIDs []string - ipAddr string - ipAddrID string - networkID string - projectID string - rules map[string]*cloudstack.LoadBalancerRule -} - -// GetLoadBalancer returns whether the specified load balancer exists, and if so, what its status is. -func (cs *CSCloud) GetLoadBalancer(ctx context.Context, clusterName string, service *v1.Service) (*v1.LoadBalancerStatus, bool, error) { - klog.V(4).Infof("GetLoadBalancer(%v, %v, %v)", clusterName, service.Namespace, service.Name) - - // Get the load balancer details and existing rules. - lb, err := cs.getLoadBalancer(service) - if err != nil { - return nil, false, err - } - - // If we don't have any rules, the load balancer does not exist. - if len(lb.rules) == 0 { - return nil, false, nil - } - - klog.V(4).Infof("Found a load balancer associated with IP %v", lb.ipAddr) - - status := &v1.LoadBalancerStatus{} - status.Ingress = append(status.Ingress, v1.LoadBalancerIngress{IP: lb.ipAddr}) - - return status, true, nil -} - -// EnsureLoadBalancer creates a new load balancer, or updates the existing one. Returns the status of the balancer. -func (cs *CSCloud) EnsureLoadBalancer(ctx context.Context, clusterName string, service *v1.Service, nodes []*v1.Node) (status *v1.LoadBalancerStatus, err error) { - klog.V(4).Infof("EnsureLoadBalancer(%v, %v, %v, %v, %v, %v)", clusterName, service.Namespace, service.Name, service.Spec.LoadBalancerIP, service.Spec.Ports, nodes) - - if len(service.Spec.Ports) == 0 { - return nil, fmt.Errorf("requested load balancer with no ports") - } - - // Get the load balancer details and existing rules. - lb, err := cs.getLoadBalancer(service) - if err != nil { - return nil, err - } - - // Set the load balancer algorithm. - switch service.Spec.SessionAffinity { - case v1.ServiceAffinityNone: - lb.algorithm = "roundrobin" - case v1.ServiceAffinityClientIP: - lb.algorithm = "source" - default: - return nil, fmt.Errorf("unsupported load balancer affinity: %v", service.Spec.SessionAffinity) - } - - // Verify that all the hosts belong to the same network, and retrieve their ID's. - lb.hostIDs, lb.networkID, err = cs.verifyHosts(nodes) - if err != nil { - return nil, err - } - - if !lb.hasLoadBalancerIP() { - // Create or retrieve the load balancer IP. - if err := lb.getLoadBalancerIP(service.Spec.LoadBalancerIP); err != nil { - return nil, err - } - - if lb.ipAddr != "" && lb.ipAddr != service.Spec.LoadBalancerIP { - defer func(lb *loadBalancer) { - if err != nil { - if err := lb.releaseLoadBalancerIP(); err != nil { - klog.Errorf(err.Error()) - } - } - }(lb) - } - } - - klog.V(4).Infof("Load balancer %v is associated with IP %v", lb.name, lb.ipAddr) - - for _, port := range service.Spec.Ports { - // All ports have their own load balancer rule, so add the port to lbName to keep the names unique. - lbRuleName := fmt.Sprintf("%s-%d", lb.name, port.Port) - - // If the load balancer rule exists and is up-to-date, we move on to the next rule. - exists, needsUpdate, err := lb.checkLoadBalancerRule(lbRuleName, port) - if err != nil { - return nil, err - } - if exists && !needsUpdate { - klog.V(4).Infof("Load balancer rule %v is up-to-date", lbRuleName) - // Delete the rule from the map, to prevent it being deleted. - delete(lb.rules, lbRuleName) - continue - } - - if needsUpdate { - klog.V(4).Infof("Updating load balancer rule: %v", lbRuleName) - if err := lb.updateLoadBalancerRule(lbRuleName); err != nil { - return nil, err - } - // Delete the rule from the map, to prevent it being deleted. - delete(lb.rules, lbRuleName) - continue - } - - klog.V(4).Infof("Creating load balancer rule: %v", lbRuleName) - lbRule, err := lb.createLoadBalancerRule(lbRuleName, port) - if err != nil { - return nil, err - } - - klog.V(4).Infof("Assigning hosts (%v) to load balancer rule: %v", lb.hostIDs, lbRuleName) - if err = lb.assignHostsToRule(lbRule, lb.hostIDs); err != nil { - return nil, err - } - - } - - // Cleanup any rules that are now still in the rules map, as they are no longer needed. - for _, lbRule := range lb.rules { - klog.V(4).Infof("Deleting obsolete load balancer rule: %v", lbRule.Name) - if err := lb.deleteLoadBalancerRule(lbRule); err != nil { - return nil, err - } - } - - status = &v1.LoadBalancerStatus{} - status.Ingress = []v1.LoadBalancerIngress{{IP: lb.ipAddr}} - - return status, nil -} - -// UpdateLoadBalancer updates hosts under the specified load balancer. -func (cs *CSCloud) UpdateLoadBalancer(ctx context.Context, clusterName string, service *v1.Service, nodes []*v1.Node) error { - klog.V(4).Infof("UpdateLoadBalancer(%v, %v, %v, %v)", clusterName, service.Namespace, service.Name, nodes) - - // Get the load balancer details and existing rules. - lb, err := cs.getLoadBalancer(service) - if err != nil { - return err - } - - // Verify that all the hosts belong to the same network, and retrieve their ID's. - lb.hostIDs, _, err = cs.verifyHosts(nodes) - if err != nil { - return err - } - - for _, lbRule := range lb.rules { - p := lb.LoadBalancer.NewListLoadBalancerRuleInstancesParams(lbRule.Id) - - // Retrieve all VMs currently associated to this load balancer rule. - l, err := lb.LoadBalancer.ListLoadBalancerRuleInstances(p) - if err != nil { - return fmt.Errorf("error retrieving associated instances: %v", err) - } - - assign, remove := symmetricDifference(lb.hostIDs, l.LoadBalancerRuleInstances) - - if len(assign) > 0 { - klog.V(4).Infof("Assigning new hosts (%v) to load balancer rule: %v", assign, lbRule.Name) - if err := lb.assignHostsToRule(lbRule, assign); err != nil { - return err - } - } - - if len(remove) > 0 { - klog.V(4).Infof("Removing old hosts (%v) from load balancer rule: %v", assign, lbRule.Name) - if err := lb.removeHostsFromRule(lbRule, remove); err != nil { - return err - } - } - } - - return nil -} - -// EnsureLoadBalancerDeleted deletes the specified load balancer if it exists, returning -// nil if the load balancer specified either didn't exist or was successfully deleted. -func (cs *CSCloud) EnsureLoadBalancerDeleted(ctx context.Context, clusterName string, service *v1.Service) error { - klog.V(4).Infof("EnsureLoadBalancerDeleted(%v, %v, %v)", clusterName, service.Namespace, service.Name) - - // Get the load balancer details and existing rules. - lb, err := cs.getLoadBalancer(service) - if err != nil { - return err - } - - for _, lbRule := range lb.rules { - klog.V(4).Infof("Deleting load balancer rule: %v", lbRule.Name) - if err := lb.deleteLoadBalancerRule(lbRule); err != nil { - return err - } - } - - if lb.ipAddr != "" && lb.ipAddr != service.Spec.LoadBalancerIP { - klog.V(4).Infof("Releasing load balancer IP: %v", lb.ipAddr) - if err := lb.releaseLoadBalancerIP(); err != nil { - return err - } - } - - return nil -} - -// GetLoadBalancerName retrieves the name of the LoadBalancer. -func (cs *CSCloud) GetLoadBalancerName(ctx context.Context, clusterName string, service *v1.Service) string { - return cloudprovider.DefaultLoadBalancerName(service) -} - -// getLoadBalancer retrieves the IP address and ID and all the existing rules it can find. -func (cs *CSCloud) getLoadBalancer(service *v1.Service) (*loadBalancer, error) { - lb := &loadBalancer{ - CloudStackClient: cs.client, - name: cs.GetLoadBalancerName(context.TODO(), "", service), - projectID: cs.projectID, - rules: make(map[string]*cloudstack.LoadBalancerRule), - } - - p := cs.client.LoadBalancer.NewListLoadBalancerRulesParams() - p.SetKeyword(lb.name) - p.SetListall(true) - - if cs.projectID != "" { - p.SetProjectid(cs.projectID) - } - - l, err := cs.client.LoadBalancer.ListLoadBalancerRules(p) - if err != nil { - return nil, fmt.Errorf("error retrieving load balancer rules: %v", err) - } - - for _, lbRule := range l.LoadBalancerRules { - lb.rules[lbRule.Name] = lbRule - - if lb.ipAddr != "" && lb.ipAddr != lbRule.Publicip { - klog.Warningf("Load balancer for service %v/%v has rules associated with different IP's: %v, %v", service.Namespace, service.Name, lb.ipAddr, lbRule.Publicip) - } - - lb.ipAddr = lbRule.Publicip - lb.ipAddrID = lbRule.Publicipid - } - - klog.V(4).Infof("Load balancer %v contains %d rule(s)", lb.name, len(lb.rules)) - - return lb, nil -} - -// verifyHosts verifies if all hosts belong to the same network, and returns the host ID's and network ID. -func (cs *CSCloud) verifyHosts(nodes []*v1.Node) ([]string, string, error) { - hostNames := map[string]bool{} - for _, node := range nodes { - hostNames[node.Name] = true - } - - p := cs.client.VirtualMachine.NewListVirtualMachinesParams() - p.SetListall(true) - - if cs.projectID != "" { - p.SetProjectid(cs.projectID) - } - - l, err := cs.client.VirtualMachine.ListVirtualMachines(p) - if err != nil { - return nil, "", fmt.Errorf("error retrieving list of hosts: %v", err) - } - - var hostIDs []string - var networkID string - - // Check if the virtual machine is in the hosts slice, then add the corresponding ID. - for _, vm := range l.VirtualMachines { - if hostNames[vm.Name] { - if networkID != "" && networkID != vm.Nic[0].Networkid { - return nil, "", fmt.Errorf("found hosts that belong to different networks") - } - - networkID = vm.Nic[0].Networkid - hostIDs = append(hostIDs, vm.Id) - } - } - - return hostIDs, networkID, nil -} - -// hasLoadBalancerIP returns true if we have a load balancer address and ID. -func (lb *loadBalancer) hasLoadBalancerIP() bool { - return lb.ipAddr != "" && lb.ipAddrID != "" -} - -// getLoadBalancerIP retieves an existing IP or associates a new IP. -func (lb *loadBalancer) getLoadBalancerIP(loadBalancerIP string) error { - if loadBalancerIP != "" { - return lb.getPublicIPAddress(loadBalancerIP) - } - - return lb.associatePublicIPAddress() -} - -// getPublicIPAddressID retrieves the ID of the given IP, and sets the address and it's ID. -func (lb *loadBalancer) getPublicIPAddress(loadBalancerIP string) error { - klog.V(4).Infof("Retrieve load balancer IP details: %v", loadBalancerIP) - - p := lb.Address.NewListPublicIpAddressesParams() - p.SetIpaddress(loadBalancerIP) - p.SetListall(true) - - if lb.projectID != "" { - p.SetProjectid(lb.projectID) - } - - l, err := lb.Address.ListPublicIpAddresses(p) - if err != nil { - return fmt.Errorf("error retrieving IP address: %v", err) - } - - if l.Count != 1 { - return fmt.Errorf("could not find IP address %v", loadBalancerIP) - } - - lb.ipAddr = l.PublicIpAddresses[0].Ipaddress - lb.ipAddrID = l.PublicIpAddresses[0].Id - - return nil -} - -// associatePublicIPAddress associates a new IP and sets the address and it's ID. -func (lb *loadBalancer) associatePublicIPAddress() error { - klog.V(4).Infof("Allocate new IP for load balancer: %v", lb.name) - // If a network belongs to a VPC, the IP address needs to be associated with - // the VPC instead of with the network. - network, count, err := lb.Network.GetNetworkByID(lb.networkID, cloudstack.WithProject(lb.projectID)) - if err != nil { - if count == 0 { - return fmt.Errorf("could not find network %v", lb.networkID) - } - return fmt.Errorf("error retrieving network: %v", err) - } - - p := lb.Address.NewAssociateIpAddressParams() - - if network.Vpcid != "" { - p.SetVpcid(network.Vpcid) - } else { - p.SetNetworkid(lb.networkID) - } - - if lb.projectID != "" { - p.SetProjectid(lb.projectID) - } - - // Associate a new IP address - r, err := lb.Address.AssociateIpAddress(p) - if err != nil { - return fmt.Errorf("error associating new IP address: %v", err) - } - - lb.ipAddr = r.Ipaddress - lb.ipAddrID = r.Id - - return nil -} - -// releasePublicIPAddress releases an associated IP. -func (lb *loadBalancer) releaseLoadBalancerIP() error { - p := lb.Address.NewDisassociateIpAddressParams(lb.ipAddrID) - - if _, err := lb.Address.DisassociateIpAddress(p); err != nil { - return fmt.Errorf("error releasing load balancer IP %v: %v", lb.ipAddr, err) - } - - return nil -} - -// checkLoadBalancerRule checks if the rule already exists and if it does, if it can be updated. If -// it does exist but cannot be updated, it will delete the existing rule so it can be created again. -func (lb *loadBalancer) checkLoadBalancerRule(lbRuleName string, port v1.ServicePort) (bool, bool, error) { - lbRule, ok := lb.rules[lbRuleName] - if !ok { - return false, false, nil - } - - // Check if any of the values we cannot update (those that require a new load balancer rule) are changed. - if lbRule.Publicip == lb.ipAddr && lbRule.Privateport == strconv.Itoa(int(port.NodePort)) && lbRule.Publicport == strconv.Itoa(int(port.Port)) { - return true, lbRule.Algorithm != lb.algorithm, nil - } - - // Delete the load balancer rule so we can create a new one using the new values. - if err := lb.deleteLoadBalancerRule(lbRule); err != nil { - return false, false, err - } - - return false, false, nil -} - -// updateLoadBalancerRule updates a load balancer rule. -func (lb *loadBalancer) updateLoadBalancerRule(lbRuleName string) error { - lbRule := lb.rules[lbRuleName] - - p := lb.LoadBalancer.NewUpdateLoadBalancerRuleParams(lbRule.Id) - p.SetAlgorithm(lb.algorithm) - - _, err := lb.LoadBalancer.UpdateLoadBalancerRule(p) - return err -} - -// createLoadBalancerRule creates a new load balancer rule and returns it's ID. -func (lb *loadBalancer) createLoadBalancerRule(lbRuleName string, port v1.ServicePort) (*cloudstack.LoadBalancerRule, error) { - p := lb.LoadBalancer.NewCreateLoadBalancerRuleParams( - lb.algorithm, - lbRuleName, - int(port.NodePort), - int(port.Port), - ) - - p.SetNetworkid(lb.networkID) - p.SetPublicipid(lb.ipAddrID) - - switch port.Protocol { - case v1.ProtocolTCP: - p.SetProtocol("TCP") - case v1.ProtocolUDP: - p.SetProtocol("UDP") - default: - return nil, fmt.Errorf("unsupported load balancer protocol: %v", port.Protocol) - } - - // Do not create corresponding firewall rule. - p.SetOpenfirewall(false) - - // Create a new load balancer rule. - r, err := lb.LoadBalancer.CreateLoadBalancerRule(p) - if err != nil { - return nil, fmt.Errorf("error creating load balancer rule %v: %v", lbRuleName, err) - } - - lbRule := &cloudstack.LoadBalancerRule{ - Id: r.Id, - Algorithm: r.Algorithm, - Cidrlist: r.Cidrlist, - Name: r.Name, - Networkid: r.Networkid, - Privateport: r.Privateport, - Publicport: r.Publicport, - Publicip: r.Publicip, - Publicipid: r.Publicipid, - } - - return lbRule, nil -} - -// deleteLoadBalancerRule deletes a load balancer rule. -func (lb *loadBalancer) deleteLoadBalancerRule(lbRule *cloudstack.LoadBalancerRule) error { - p := lb.LoadBalancer.NewDeleteLoadBalancerRuleParams(lbRule.Id) - - if _, err := lb.LoadBalancer.DeleteLoadBalancerRule(p); err != nil { - return fmt.Errorf("error deleting load balancer rule %v: %v", lbRule.Name, err) - } - - // Delete the rule from the map as it no longer exists - delete(lb.rules, lbRule.Name) - - return nil -} - -// assignHostsToRule assigns hosts to a load balancer rule. -func (lb *loadBalancer) assignHostsToRule(lbRule *cloudstack.LoadBalancerRule, hostIDs []string) error { - p := lb.LoadBalancer.NewAssignToLoadBalancerRuleParams(lbRule.Id) - p.SetVirtualmachineids(hostIDs) - - if _, err := lb.LoadBalancer.AssignToLoadBalancerRule(p); err != nil { - return fmt.Errorf("error assigning hosts to load balancer rule %v: %v", lbRule.Name, err) - } - - return nil -} - -// removeHostsFromRule removes hosts from a load balancer rule. -func (lb *loadBalancer) removeHostsFromRule(lbRule *cloudstack.LoadBalancerRule, hostIDs []string) error { - p := lb.LoadBalancer.NewRemoveFromLoadBalancerRuleParams(lbRule.Id) - p.SetVirtualmachineids(hostIDs) - - if _, err := lb.LoadBalancer.RemoveFromLoadBalancerRule(p); err != nil { - return fmt.Errorf("error removing hosts from load balancer rule %v: %v", lbRule.Name, err) - } - - return nil -} - -// symmetricDifference returns the symmetric difference between the old (existing) and new (wanted) host ID's. -func symmetricDifference(hostIDs []string, lbInstances []*cloudstack.VirtualMachine) ([]string, []string) { - new := make(map[string]bool) - for _, hostID := range hostIDs { - new[hostID] = true - } - - var remove []string - for _, instance := range lbInstances { - if new[instance.Id] { - delete(new, instance.Id) - continue - } - - remove = append(remove, instance.Id) - } - - var assign []string - for hostID := range new { - assign = append(assign, hostID) - } - - return assign, remove -} diff --git a/pkg/cloudprovider/providers/cloudstack/cloudstack_test.go b/pkg/cloudprovider/providers/cloudstack/cloudstack_test.go deleted file mode 100644 index 2a7b3a2bb8b..00000000000 --- a/pkg/cloudprovider/providers/cloudstack/cloudstack_test.go +++ /dev/null @@ -1,118 +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 cloudstack - -import ( - "context" - "os" - "strconv" - "strings" - "testing" - - "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -const testClusterName = "testCluster" - -func TestReadConfig(t *testing.T) { - _, err := readConfig(nil) - if err != nil { - t.Fatalf("Should not return an error when no config is provided: %v", err) - } - - cfg, err := readConfig(strings.NewReader(` - [Global] - api-url = https://cloudstack.url - api-key = a-valid-api-key - secret-key = a-valid-secret-key - ssl-no-verify = true - project-id = a-valid-project-id - `)) - if err != nil { - t.Fatalf("Should succeed when a valid config is provided: %v", err) - } - - if cfg.Global.APIURL != "https://cloudstack.url" { - t.Errorf("incorrect api-url: %s", cfg.Global.APIURL) - } - if cfg.Global.APIKey != "a-valid-api-key" { - t.Errorf("incorrect api-key: %s", cfg.Global.APIKey) - } - if cfg.Global.SecretKey != "a-valid-secret-key" { - t.Errorf("incorrect secret-key: %s", cfg.Global.SecretKey) - } - if !cfg.Global.SSLNoVerify { - t.Errorf("incorrect ssl-no-verify: %t", cfg.Global.SSLNoVerify) - } -} - -// This allows acceptance testing against an existing CloudStack environment. -func configFromEnv() (*CSConfig, bool) { - cfg := &CSConfig{} - - cfg.Global.APIURL = os.Getenv("CS_API_URL") - cfg.Global.APIKey = os.Getenv("CS_API_KEY") - cfg.Global.SecretKey = os.Getenv("CS_SECRET_KEY") - cfg.Global.ProjectID = os.Getenv("CS_PROJECT_ID") - - // It is save to ignore the error here. If the input cannot be parsed SSLNoVerify - // will still be a bool with its zero value (false) which is the expected default. - cfg.Global.SSLNoVerify, _ = strconv.ParseBool(os.Getenv("CS_SSL_NO_VERIFY")) - - // Check if we have the minimum required info to be able to connect to CloudStack. - ok := cfg.Global.APIURL != "" && cfg.Global.APIKey != "" && cfg.Global.SecretKey != "" - - return cfg, ok -} - -func TestNewCSCloud(t *testing.T) { - cfg, ok := configFromEnv() - if !ok { - t.Skipf("No config found in environment") - } - - _, err := newCSCloud(cfg) - if err != nil { - t.Fatalf("Failed to construct/authenticate CloudStack: %v", err) - } -} - -func TestLoadBalancer(t *testing.T) { - cfg, ok := configFromEnv() - if !ok { - t.Skipf("No config found in environment") - } - - cs, err := newCSCloud(cfg) - if err != nil { - t.Fatalf("Failed to construct/authenticate CloudStack: %v", err) - } - - lb, ok := cs.LoadBalancer() - if !ok { - t.Fatalf("LoadBalancer() returned false") - } - - _, exists, err := lb.GetLoadBalancer(context.TODO(), testClusterName, &v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "noexist"}}) - if err != nil { - t.Fatalf("GetLoadBalancer(\"noexist\") returned error: %s", err) - } - if exists { - t.Fatalf("GetLoadBalancer(\"noexist\") returned exists") - } -} diff --git a/pkg/cloudprovider/providers/cloudstack/metadata.go b/pkg/cloudprovider/providers/cloudstack/metadata.go deleted file mode 100644 index b666e076dd5..00000000000 --- a/pkg/cloudprovider/providers/cloudstack/metadata.go +++ /dev/null @@ -1,226 +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 cloudstack - -import ( - "context" - "errors" - "fmt" - "io/ioutil" - "net" - "net/http" - - "github.com/d2g/dhcp4" - "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" - cloudprovider "k8s.io/cloud-provider" - "k8s.io/klog" -) - -var _ cloudprovider.Instances = (*metadata)(nil) -var _ cloudprovider.Zones = (*metadata)(nil) - -type metadata struct { - dhcpServer string - zone string -} - -type metadataType string - -const ( - metadataTypeHostname metadataType = "local-hostname" - metadataTypeExternalIP metadataType = "public-ipv4" - metadataTypeInternalIP metadataType = "local-ipv4" - metadataTypeInstanceID metadataType = "instance-id" - metadataTypeInstanceType metadataType = "service-offering" - metadataTypeZone metadataType = "availability-zone" -) - -// NodeAddresses returns the addresses of the specified instance. -func (m *metadata) NodeAddresses(ctx context.Context, name types.NodeName) ([]v1.NodeAddress, error) { - externalIP, err := m.get(metadataTypeExternalIP) - if err != nil { - return nil, fmt.Errorf("could not get external IP: %v", err) - } - - internalIP, err := m.get(metadataTypeInternalIP) - if err != nil { - return nil, fmt.Errorf("could not get internal IP: %v", err) - } - - addresses := []v1.NodeAddress{ - {Type: v1.NodeExternalIP, Address: externalIP}, - {Type: v1.NodeInternalIP, Address: internalIP}, - } - - hostname, err := m.get(metadataTypeHostname) - if err != nil { - return nil, fmt.Errorf("could not get hostname: %v", err) - } - if hostname != "" { - addresses = append(addresses, v1.NodeAddress{Type: v1.NodeHostName, Address: hostname}) - } - - return addresses, nil -} - -// NodeAddressesByProviderID returns the addresses of the specified instance. -func (m *metadata) NodeAddressesByProviderID(ctx context.Context, providerID string) ([]v1.NodeAddress, error) { - return nil, errors.New("NodeAddressesByProviderID not implemented") -} - -// InstanceID returns the cloud provider ID of the specified instance. -func (m *metadata) InstanceID(ctx context.Context, name types.NodeName) (string, error) { - instanceID, err := m.get(metadataTypeInstanceID) - if err != nil { - return "", fmt.Errorf("could not get instance ID: %v", err) - } - - zone, err := m.get(metadataTypeZone) - if err != nil { - return "", fmt.Errorf("could not get zone: %v", err) - } - - return "/" + zone + "/" + instanceID, nil -} - -// InstanceType returns the type of the specified instance. -func (m *metadata) InstanceType(ctx context.Context, name types.NodeName) (string, error) { - instanceType, err := m.get(metadataTypeInstanceType) - if err != nil { - return "", fmt.Errorf("could not get instance type: %v", err) - } - - return instanceType, nil -} - -// InstanceTypeByProviderID returns the type of the specified instance. -func (m *metadata) InstanceTypeByProviderID(ctx context.Context, providerID string) (string, error) { - return "", errors.New("InstanceTypeByProviderID not implemented") -} - -// AddSSHKeyToAllInstances is currently not implemented. -func (m *metadata) AddSSHKeyToAllInstances(ctx context.Context, user string, keyData []byte) error { - return cloudprovider.NotImplemented -} - -// CurrentNodeName returns the name of the node we are currently running on. -func (m *metadata) CurrentNodeName(ctx context.Context, hostname string) (types.NodeName, error) { - return types.NodeName(hostname), nil -} - -// InstanceExistsByProviderID returns if the instance still exists. -func (m *metadata) InstanceExistsByProviderID(ctx context.Context, providerID string) (bool, error) { - return false, errors.New("InstanceExistsByProviderID not implemented") -} - -// InstanceShutdownByProviderID returns if the instance is shutdown. -func (m *metadata) InstanceShutdownByProviderID(ctx context.Context, providerID string) (bool, error) { - return false, cloudprovider.NotImplemented -} - -// GetZone returns the Zone containing the region that the program is running in. -func (m *metadata) GetZone(ctx context.Context) (cloudprovider.Zone, error) { - zone := cloudprovider.Zone{} - - if m.zone == "" { - zoneName, err := m.get(metadataTypeZone) - if err != nil { - return zone, fmt.Errorf("could not get zone: %v", err) - } - - m.zone = zoneName - } - - klog.V(2).Infof("Current zone is %v", zone) - zone.FailureDomain = m.zone - zone.Region = m.zone - - return zone, nil -} - -// GetZoneByProviderID returns the Zone, found by using the provider ID. -func (m *metadata) GetZoneByProviderID(ctx context.Context, providerID string) (cloudprovider.Zone, error) { - return cloudprovider.Zone{}, errors.New("GetZoneByProviderID not implemented") -} - -// GetZoneByNodeName returns the Zone, found by using the node name. -func (m *metadata) GetZoneByNodeName(ctx context.Context, nodeName types.NodeName) (cloudprovider.Zone, error) { - return cloudprovider.Zone{}, errors.New("GetZoneByNodeName not implemented") -} - -func (m *metadata) get(mdType metadataType) (string, error) { - url := fmt.Sprintf("http://%s/latest/meta-data/%s", m.dhcpServer, mdType) - - resp, err := http.Get(url) - if err != nil { - return "", fmt.Errorf("error reading metadata: %v", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return "", fmt.Errorf("unexpected HTTP status: %d", resp.StatusCode) - } - - data, err := ioutil.ReadAll(resp.Body) - if err != nil { - return "", fmt.Errorf("error reading response body: %d", resp.StatusCode) - } - - return string(data), nil -} - -func findDHCPServer() (string, error) { - nics, err := net.Interfaces() - if err != nil { - return "", fmt.Errorf("could not get interfaces: %v", err) - } - - for _, nic := range nics { - if nic.Flags&net.FlagUp == 1 && nic.Flags&net.FlagLoopback == 0 && nic.Flags&net.FlagPointToPoint == 0 { - addrs, err := nic.Addrs() - if err != nil { - return "", fmt.Errorf("error reading IP addresses from interface %v: %v", nic.Name, err) - } - - if addrs != nil { - client, err := newDHCPClient(&nic) - if err != nil { - return "", fmt.Errorf("error creating new DHCP client: %v", err) - } - - discoverPacket, err := client.SendDiscoverPacket() - if err != nil { - return "", fmt.Errorf("error sending DHCP discover package: %v", err) - } - - offerPacket, err := client.GetOffer(&discoverPacket) - if err != nil { - return "", fmt.Errorf("error receiving DHCP offer package: %v", err) - } - - offerPacketOptions := offerPacket.ParseOptions() - - if ipaddr, ok := offerPacketOptions[dhcp4.OptionServerIdentifier]; ok { - return net.IP(ipaddr).String(), nil - } - } - } - } - - return "", errors.New("no server found") -} diff --git a/pkg/cloudprovider/providers/cloudstack/metadata_linux.go b/pkg/cloudprovider/providers/cloudstack/metadata_linux.go deleted file mode 100644 index 02603bf9479..00000000000 --- a/pkg/cloudprovider/providers/cloudstack/metadata_linux.go +++ /dev/null @@ -1,40 +0,0 @@ -// +build linux - -/* -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 cloudstack - -import ( - "net" - "time" - - "github.com/d2g/dhcp4client" -) - -func newDHCPClient(nic *net.Interface) (*dhcp4client.Client, error) { - pktsock, err := dhcp4client.NewPacketSock(nic.Index) - if err != nil { - return nil, err - } - - return dhcp4client.New( - dhcp4client.HardwareAddr(nic.HardwareAddr), - dhcp4client.Timeout(2*time.Second), - dhcp4client.Broadcast(false), - dhcp4client.Connection(pktsock), - ) -} diff --git a/pkg/cloudprovider/providers/cloudstack/metadata_other.go b/pkg/cloudprovider/providers/cloudstack/metadata_other.go deleted file mode 100644 index 74a7fe221f3..00000000000 --- a/pkg/cloudprovider/providers/cloudstack/metadata_other.go +++ /dev/null @@ -1,40 +0,0 @@ -// +build !linux - -/* -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 cloudstack - -import ( - "net" - "time" - - "github.com/d2g/dhcp4client" -) - -func newDHCPClient(nic *net.Interface) (*dhcp4client.Client, error) { - inetsock, err := dhcp4client.NewInetSock() - if err != nil { - return nil, err - } - - return dhcp4client.New( - dhcp4client.HardwareAddr(nic.HardwareAddr), - dhcp4client.Timeout(2*time.Second), - dhcp4client.Broadcast(false), - dhcp4client.Connection(inetsock), - ) -} diff --git a/pkg/cloudprovider/providers/ovirt/BUILD b/pkg/cloudprovider/providers/ovirt/BUILD deleted file mode 100644 index 4cdbb098800..00000000000 --- a/pkg/cloudprovider/providers/ovirt/BUILD +++ /dev/null @@ -1,39 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", - "go_test", -) - -go_library( - name = "go_default_library", - srcs = ["ovirt.go"], - importpath = "k8s.io/kubernetes/pkg/cloudprovider/providers/ovirt", - deps = [ - "//staging/src/k8s.io/api/core/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", - "//staging/src/k8s.io/cloud-provider:go_default_library", - "//vendor/gopkg.in/gcfg.v1:go_default_library", - ], -) - -go_test( - name = "go_default_test", - srcs = ["ovirt_test.go"], - embed = [":go_default_library"], - deps = ["//staging/src/k8s.io/cloud-provider:go_default_library"], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [":package-srcs"], - tags = ["automanaged"], -) diff --git a/pkg/cloudprovider/providers/ovirt/ovirt.go b/pkg/cloudprovider/providers/ovirt/ovirt.go deleted file mode 100644 index c9364a2585b..00000000000 --- a/pkg/cloudprovider/providers/ovirt/ovirt.go +++ /dev/null @@ -1,329 +0,0 @@ -/* -Copyright 2014 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 ovirt - -import ( - "context" - "encoding/xml" - "fmt" - "io" - "io/ioutil" - "net" - "net/http" - "net/url" - "path" - "sort" - "strings" - - "gopkg.in/gcfg.v1" - - "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" - cloudprovider "k8s.io/cloud-provider" -) - -// ProviderName is the name of this cloud provider. -const ProviderName = "ovirt" - -// Instance specifies UUID, name and IP address of the instance. -type Instance struct { - UUID string - Name string - IPAddress string -} - -// InstanceMap provides the map of Ovirt instances. -type InstanceMap map[string]Instance - -var _ cloudprovider.Interface = (*Cloud)(nil) -var _ cloudprovider.Instances = (*Cloud)(nil) - -// Cloud is an implementation of the cloud provider interface for Ovirt. -type Cloud struct { - VmsRequest *url.URL - HostsRequest *url.URL -} - -// APIConfig wraps the api settings for the Ovirt. -type APIConfig struct { - Connection struct { - APIEntry string `gcfg:"uri"` - Username string `gcfg:"username"` - Password string `gcfg:"password"` - } - Filters struct { - VmsQuery string `gcfg:"vms"` - } -} - -// XMLVMAddress is an implementation for the Ovirt instance IP address in xml. -type XMLVMAddress struct { - Address string `xml:"address,attr"` -} - -// XMLVMInfo is an implementation for the Ovirt instance details in xml. -type XMLVMInfo struct { - UUID string `xml:"id,attr"` - Name string `xml:"name"` - Hostname string `xml:"guest_info>fqdn"` - Addresses []XMLVMAddress `xml:"guest_info>ips>ip"` - State string `xml:"status>state"` -} - -// XMLVmsList is an implementation to provide the list of Ovirt instances. -type XMLVmsList struct { - XMLName xml.Name `xml:"vms"` - VM []XMLVMInfo `xml:"vm"` -} - -func init() { - cloudprovider.RegisterCloudProvider(ProviderName, - func(config io.Reader) (cloudprovider.Interface, error) { - return newOVirtCloud(config) - }) -} - -func newOVirtCloud(config io.Reader) (*Cloud, error) { - if config == nil { - return nil, fmt.Errorf("missing configuration file for ovirt cloud provider") - } - - oVirtConfig := APIConfig{} - - /* defaults */ - oVirtConfig.Connection.Username = "admin@internal" - - if err := gcfg.ReadInto(&oVirtConfig, config); err != nil { - return nil, err - } - - if oVirtConfig.Connection.APIEntry == "" { - return nil, fmt.Errorf("missing ovirt uri in cloud provider configuration") - } - - request, err := url.Parse(oVirtConfig.Connection.APIEntry) - if err != nil { - return nil, err - } - - request.Path = path.Join(request.Path, "vms") - request.User = url.UserPassword(oVirtConfig.Connection.Username, oVirtConfig.Connection.Password) - request.RawQuery = url.Values{"search": {oVirtConfig.Filters.VmsQuery}}.Encode() - - return &Cloud{VmsRequest: request}, nil -} - -// Initialize passes a Kubernetes clientBuilder interface to the cloud provider -func (v *Cloud) Initialize(clientBuilder cloudprovider.ControllerClientBuilder, stop <-chan struct{}) { -} - -// Clusters returns the list of clusters. -func (v *Cloud) Clusters() (cloudprovider.Clusters, bool) { - return nil, false -} - -// ProviderName returns the cloud provider ID. -func (v *Cloud) ProviderName() string { - return ProviderName -} - -// HasClusterID returns true if the cluster has a clusterID -func (v *Cloud) HasClusterID() bool { - return true -} - -// LoadBalancer returns an implementation of LoadBalancer for oVirt cloud -func (v *Cloud) LoadBalancer() (cloudprovider.LoadBalancer, bool) { - return nil, false -} - -// Instances returns an implementation of Instances for oVirt cloud -func (v *Cloud) Instances() (cloudprovider.Instances, bool) { - return v, true -} - -// Zones returns an implementation of Zones for oVirt cloud -func (v *Cloud) Zones() (cloudprovider.Zones, bool) { - return nil, false -} - -// Routes returns an implementation of Routes for oVirt cloud -func (v *Cloud) Routes() (cloudprovider.Routes, bool) { - return nil, false -} - -// NodeAddresses returns the NodeAddresses of the instance with the specified nodeName. -func (v *Cloud) NodeAddresses(ctx context.Context, nodeName types.NodeName) ([]v1.NodeAddress, error) { - name := mapNodeNameToInstanceName(nodeName) - instance, err := v.fetchInstance(name) - if err != nil { - return nil, err - } - - var address net.IP - - if instance.IPAddress != "" { - address = net.ParseIP(instance.IPAddress) - if address == nil { - return nil, fmt.Errorf("couldn't parse address: %s", instance.IPAddress) - } - } else { - resolved, err := net.LookupIP(name) - if err != nil || len(resolved) < 1 { - return nil, fmt.Errorf("couldn't lookup address: %s", name) - } - address = resolved[0] - } - - return []v1.NodeAddress{ - {Type: v1.NodeInternalIP, Address: address.String()}, - {Type: v1.NodeExternalIP, Address: address.String()}, - }, nil -} - -// NodeAddressesByProviderID returns the node addresses of an instances with the specified unique providerID -// This method will not be called from the node that is requesting this ID. i.e. metadata service -// and other local methods cannot be used here -func (v *Cloud) NodeAddressesByProviderID(ctx context.Context, providerID string) ([]v1.NodeAddress, error) { - return []v1.NodeAddress{}, cloudprovider.NotImplemented -} - -// mapNodeNameToInstanceName maps from a k8s NodeName to an ovirt instance name (the hostname) -// This is a simple string cast -func mapNodeNameToInstanceName(nodeName types.NodeName) string { - return string(nodeName) -} - -// InstanceExistsByProviderID returns true if the instance with the given provider id still exists and is running. -// If false is returned with no error, the instance will be immediately deleted by the cloud controller manager. -func (v *Cloud) InstanceExistsByProviderID(ctx context.Context, providerID string) (bool, error) { - return false, cloudprovider.NotImplemented -} - -// InstanceShutdownByProviderID returns true if the instance is in safe state to detach volumes -func (v *Cloud) InstanceShutdownByProviderID(ctx context.Context, providerID string) (bool, error) { - return false, cloudprovider.NotImplemented -} - -// InstanceID returns the cloud provider ID of the node with the specified NodeName. -func (v *Cloud) InstanceID(ctx context.Context, nodeName types.NodeName) (string, error) { - name := mapNodeNameToInstanceName(nodeName) - instance, err := v.fetchInstance(name) - if err != nil { - return "", err - } - // TODO: define a way to identify the provider instance to complete - // the format /. - return "/" + instance.UUID, err -} - -// InstanceTypeByProviderID returns the cloudprovider instance type of the node with the specified unique providerID -// This method will not be called from the node that is requesting this ID. i.e. metadata service -// and other local methods cannot be used here -func (v *Cloud) InstanceTypeByProviderID(ctx context.Context, providerID string) (string, error) { - return "", cloudprovider.NotImplemented -} - -// InstanceType returns the type of the specified instance. -func (v *Cloud) InstanceType(ctx context.Context, name types.NodeName) (string, error) { - return "", nil -} - -func getInstancesFromXML(body io.Reader) (InstanceMap, error) { - if body == nil { - return nil, fmt.Errorf("ovirt rest-api response body is missing") - } - - content, err := ioutil.ReadAll(body) - if err != nil { - return nil, err - } - - vmlist := XMLVmsList{} - - if err := xml.Unmarshal(content, &vmlist); err != nil { - return nil, err - } - - instances := make(InstanceMap) - - for _, vm := range vmlist.VM { - // Always return only vms that are up and running - if vm.Hostname != "" && strings.ToLower(vm.State) == "up" { - address := "" - if len(vm.Addresses) > 0 { - address = vm.Addresses[0].Address - } - - instances[vm.Hostname] = Instance{ - UUID: vm.UUID, - Name: vm.Name, - IPAddress: address, - } - } - } - - return instances, nil -} - -func (v *Cloud) fetchAllInstances() (InstanceMap, error) { - response, err := http.Get(v.VmsRequest.String()) - if err != nil { - return nil, err - } - - defer response.Body.Close() - - return getInstancesFromXML(response.Body) -} - -func (v *Cloud) fetchInstance(name string) (*Instance, error) { - allInstances, err := v.fetchAllInstances() - if err != nil { - return nil, err - } - - instance, found := allInstances[name] - if !found { - return nil, fmt.Errorf("cannot find instance: %s", name) - } - - return &instance, nil -} - -// ListSortedNames returns the list of sorted Ovirt instances name. -func (m *InstanceMap) ListSortedNames() []string { - var names []string - - for k := range *m { - names = append(names, k) - } - - sort.Strings(names) - - return names -} - -// CurrentNodeName is implementation of Instances.CurrentNodeName. -func (v *Cloud) CurrentNodeName(ctx context.Context, hostname string) (types.NodeName, error) { - return types.NodeName(hostname), nil -} - -// AddSSHKeyToAllInstances is currently not implemented. -func (v *Cloud) AddSSHKeyToAllInstances(ctx context.Context, user string, keyData []byte) error { - return cloudprovider.NotImplemented -} diff --git a/pkg/cloudprovider/providers/ovirt/ovirt_test.go b/pkg/cloudprovider/providers/ovirt/ovirt_test.go deleted file mode 100644 index 4ecbe40f6a5..00000000000 --- a/pkg/cloudprovider/providers/ovirt/ovirt_test.go +++ /dev/null @@ -1,126 +0,0 @@ -/* -Copyright 2014 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 ovirt - -import ( - "io" - "strings" - "testing" - - cloudprovider "k8s.io/cloud-provider" -) - -func TestOVirtCloudConfiguration(t *testing.T) { - config1 := (io.Reader)(nil) - - _, err1 := cloudprovider.GetCloudProvider("ovirt", config1) - if err1 == nil { - t.Fatalf("An error is expected when the configuration is missing") - } - - config2 := strings.NewReader("") - - _, err2 := cloudprovider.GetCloudProvider("ovirt", config2) - if err2 == nil { - t.Fatalf("An error is expected when the configuration is empty") - } - - config3 := strings.NewReader(` -[connection] - `) - - _, err3 := cloudprovider.GetCloudProvider("ovirt", config3) - if err3 == nil { - t.Fatalf("An error is expected when the uri is missing") - } - - config4 := strings.NewReader(` -[connection] -uri = https://localhost:8443/ovirt-engine/api -`) - - _, err4 := cloudprovider.GetCloudProvider("ovirt", config4) - if err4 != nil { - t.Fatalf("Unexpected error creating the provider: %s", err4) - } -} - -func TestOVirtCloudXmlParsing(t *testing.T) { - body1 := (io.Reader)(nil) - - _, err1 := getInstancesFromXML(body1) - if err1 == nil { - t.Fatalf("An error is expected when body is missing") - } - - body2 := strings.NewReader("") - - _, err2 := getInstancesFromXML(body2) - if err2 == nil { - t.Fatalf("An error is expected when body is empty") - } - - body3 := strings.NewReader(` - - - -`) - - instances3, err3 := getInstancesFromXML(body3) - if err3 != nil { - t.Fatalf("Unexpected error listing instances: %s", err3) - } - if len(instances3) > 0 { - t.Fatalf("Unexpected number of instance(s): %d", len(instances3)) - } - - body4 := strings.NewReader(` - - - Up - host1 - - - - - - Up - - - Down - host2 - - - Up - host3 - - -`) - - instances4, err4 := getInstancesFromXML(body4) - if err4 != nil { - t.Fatalf("Unexpected error listing instances: %s", err4) - } - if len(instances4) != 2 { - t.Fatalf("Unexpected number of instance(s): %d", len(instances4)) - } - - names := instances4.ListSortedNames() - if names[0] != "host1" || names[1] != "host3" { - t.Fatalf("Unexpected instance(s): %s", instances4) - } -} diff --git a/pkg/cloudprovider/providers/photon/BUILD b/pkg/cloudprovider/providers/photon/BUILD deleted file mode 100644 index 62a6174993d..00000000000 --- a/pkg/cloudprovider/providers/photon/BUILD +++ /dev/null @@ -1,46 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", - "go_test", -) - -go_library( - name = "go_default_library", - srcs = ["photon.go"], - importpath = "k8s.io/kubernetes/pkg/cloudprovider/providers/photon", - deps = [ - "//staging/src/k8s.io/api/core/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", - "//staging/src/k8s.io/cloud-provider:go_default_library", - "//staging/src/k8s.io/cloud-provider/node/helpers:go_default_library", - "//vendor/github.com/vmware/photon-controller-go-sdk/photon:go_default_library", - "//vendor/gopkg.in/gcfg.v1:go_default_library", - "//vendor/k8s.io/klog:go_default_library", - ], -) - -go_test( - name = "go_default_test", - srcs = ["photon_test.go"], - embed = [":go_default_library"], - deps = [ - "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/util/rand:go_default_library", - "//staging/src/k8s.io/cloud-provider:go_default_library", - ], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [":package-srcs"], - tags = ["automanaged"], -) diff --git a/pkg/cloudprovider/providers/photon/OWNERS b/pkg/cloudprovider/providers/photon/OWNERS deleted file mode 100644 index 36c201edd9a..00000000000 --- a/pkg/cloudprovider/providers/photon/OWNERS +++ /dev/null @@ -1,6 +0,0 @@ -# See the OWNERS docs at https://go.k8s.io/owners - -maintainers: -- luomiao -- kerneltime -- abrarshivani diff --git a/pkg/cloudprovider/providers/photon/photon.go b/pkg/cloudprovider/providers/photon/photon.go deleted file mode 100644 index f3b85e9a145..00000000000 --- a/pkg/cloudprovider/providers/photon/photon.go +++ /dev/null @@ -1,734 +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. -*/ - -// This version of Photon cloud provider supports the disk interface -// for Photon persistent disk volume plugin. LoadBalancer, Routes, and -// Zones are currently not supported. -// The use of Photon cloud provider requires to start kubelet, kube-apiserver, -// and kube-controller-manager with config flag: '--cloud-provider=photon -// --cloud-config=[path_to_config_file]'. When running multi-node kubernetes -// using docker, the config file should be located inside /etc/kubernetes. -package photon - -import ( - "bufio" - "context" - "errors" - "fmt" - "io" - "log" - "net" - "os" - "strings" - - "github.com/vmware/photon-controller-go-sdk/photon" - "gopkg.in/gcfg.v1" - "k8s.io/api/core/v1" - k8stypes "k8s.io/apimachinery/pkg/types" - cloudprovider "k8s.io/cloud-provider" - nodehelpers "k8s.io/cloud-provider/node/helpers" - "k8s.io/klog" -) - -const ( - ProviderName = "photon" - DiskSpecKind = "persistent-disk" - MAC_OUI_VC = "00:50:56" - MAC_OUI_ESX = "00:0c:29" -) - -// overrideIP indicates if the hostname is overridden by IP address, such as when -// running multi-node kubernetes using docker. In this case the user should set -// overrideIP = true in cloud config file. Default value is false. -var overrideIP = false - -var _ cloudprovider.Interface = (*PCCloud)(nil) -var _ cloudprovider.Instances = (*PCCloud)(nil) -var _ cloudprovider.Zones = (*PCCloud)(nil) - -// PCCloud is an implementation of the cloud provider interface for Photon Controller. -type PCCloud struct { - cfg *PCConfig - // InstanceID of the server where this PCCloud object is instantiated. - localInstanceID string - // local $HOSTNAME - localHostname string - // hostname from K8S, could be overridden - localK8sHostname string - // Photon project ID. We assume that there is only one Photon Controller project - // in the environment per current Photon Controller deployment methodology. - projID string - cloudprovider.Zone - photonClient *photon.Client - logger *log.Logger -} - -type PCConfig struct { - Global struct { - // the Photon Controller endpoint IP address - CloudTarget string `gcfg:"target"` - // Photon Controller project name - Project string `gcfg:"project"` - // when kubelet is started with '--hostname-override=${IP_ADDRESS}', set to true; - // otherwise, set to false. - OverrideIP bool `gcfg:"overrideIP"` - // VM ID for this node - VMID string `gcfg:"vmID"` - // Authentication enabled or not - AuthEnabled bool `gcfg:"authentication"` - } -} - -// Disks is interface for manipulation with PhotonController Persistent Disks. -type Disks interface { - // AttachDisk attaches given disk to given node. Current node - // is used when nodeName is empty string. - AttachDisk(ctx context.Context, pdID string, nodeName k8stypes.NodeName) error - - // DetachDisk detaches given disk to given node. Current node - // is used when nodeName is empty string. - DetachDisk(ctx context.Context, pdID string, nodeName k8stypes.NodeName) error - - // DiskIsAttached checks if a disk is attached to the given node. - DiskIsAttached(ctx context.Context, pdID string, nodeName k8stypes.NodeName) (bool, error) - - // DisksAreAttached is a batch function to check if a list of disks are attached - // to the node with the specified NodeName. - DisksAreAttached(ctx context.Context, pdIDs []string, nodeName k8stypes.NodeName) (map[string]bool, error) - - // CreateDisk creates a new PD with given properties. - CreateDisk(volumeOptions *VolumeOptions) (pdID string, err error) - - // DeleteDisk deletes PD. - DeleteDisk(pdID string) error -} - -// VolumeOptions specifies capacity, tags, name and flavorID for a volume. -type VolumeOptions struct { - CapacityGB int - Tags map[string]string - Name string - Flavor string -} - -func readConfig(config io.Reader) (PCConfig, error) { - if config == nil { - err := fmt.Errorf("cloud provider config file is missing. Please restart kubelet with --cloud-provider=photon --cloud-config=[path_to_config_file]") - return PCConfig{}, err - } - - var cfg PCConfig - err := gcfg.ReadInto(&cfg, config) - return cfg, err -} - -func init() { - cloudprovider.RegisterCloudProvider(ProviderName, func(config io.Reader) (cloudprovider.Interface, error) { - cfg, err := readConfig(config) - if err != nil { - klog.Errorf("Photon Cloud Provider: failed to read in cloud provider config file. Error[%v]", err) - return nil, err - } - return newPCCloud(cfg) - }) -} - -// Retrieve the Photon VM ID from the Photon Controller endpoint based on the node name -func getVMIDbyNodename(pc *PCCloud, nodeName string) (string, error) { - photonClient, err := getPhotonClient(pc) - if err != nil { - klog.Errorf("Photon Cloud Provider: Failed to get photon client for getVMIDbyNodename, error: [%v]", err) - return "", err - } - - vmList, err := photonClient.Projects.GetVMs(pc.projID, nil) - if err != nil { - klog.Errorf("Photon Cloud Provider: Failed to GetVMs from project %s with nodeName %s, error: [%v]", pc.projID, nodeName, err) - return "", err - } - - for _, vm := range vmList.Items { - if vm.Name == nodeName { - return vm.ID, nil - } - } - - return "", fmt.Errorf("No matching started VM is found with name %s", nodeName) -} - -// Retrieve the Photon VM ID from the Photon Controller endpoint based on the IP address -func getVMIDbyIP(pc *PCCloud, IPAddress string) (string, error) { - photonClient, err := getPhotonClient(pc) - if err != nil { - klog.Errorf("Photon Cloud Provider: Failed to get photon client for getVMIDbyNodename, error: [%v]", err) - return "", err - } - - vmList, err := photonClient.Projects.GetVMs(pc.projID, nil) - if err != nil { - klog.Errorf("Photon Cloud Provider: Failed to GetVMs for project %s. error: [%v]", pc.projID, err) - return "", err - } - - for _, vm := range vmList.Items { - task, err := photonClient.VMs.GetNetworks(vm.ID) - if err != nil { - klog.Warningf("Photon Cloud Provider: GetNetworks failed for vm.ID %s, error [%v]", vm.ID, err) - } else { - task, err = photonClient.Tasks.Wait(task.ID) - if err != nil { - klog.Warningf("Photon Cloud Provider: Wait task for GetNetworks failed for vm.ID %s, error [%v]", vm.ID, err) - } else { - networkConnections := task.ResourceProperties.(map[string]interface{}) - networks := networkConnections["networkConnections"].([]interface{}) - for _, nt := range networks { - network := nt.(map[string]interface{}) - if val, ok := network["ipAddress"]; ok && val != nil { - ipAddr := val.(string) - if ipAddr == IPAddress { - return vm.ID, nil - } - } - } - } - } - } - - return "", fmt.Errorf("No matching VM is found with IP %s", IPAddress) -} - -func getPhotonClient(pc *PCCloud) (*photon.Client, error) { - var err error - if len(pc.cfg.Global.CloudTarget) == 0 { - return nil, fmt.Errorf("Photon Controller endpoint was not specified") - } - - options := &photon.ClientOptions{ - IgnoreCertificate: true, - } - - pc.photonClient = photon.NewClient(pc.cfg.Global.CloudTarget, options, pc.logger) - if pc.cfg.Global.AuthEnabled == true { - // work around before metadata is available - file, err := os.Open("/etc/kubernetes/pc_login_info") - if err != nil { - klog.Errorf("Photon Cloud Provider: Authentication is enabled but found no username/password at /etc/kubernetes/pc_login_info. Error[%v]", err) - return nil, err - } - defer file.Close() - scanner := bufio.NewScanner(file) - if !scanner.Scan() { - klog.Error("Photon Cloud Provider: Empty username inside /etc/kubernetes/pc_login_info.") - return nil, fmt.Errorf("Failed to create authentication enabled client with invalid username") - } - username := scanner.Text() - if !scanner.Scan() { - klog.Error("Photon Cloud Provider: Empty password set inside /etc/kubernetes/pc_login_info.") - return nil, fmt.Errorf("Failed to create authentication enabled client with invalid password") - } - password := scanner.Text() - - tokenOptions, err := pc.photonClient.Auth.GetTokensByPassword(username, password) - if err != nil { - klog.Error("Photon Cloud Provider: failed to get tokens by password") - return nil, err - } - - options = &photon.ClientOptions{ - IgnoreCertificate: true, - TokenOptions: &photon.TokenOptions{ - AccessToken: tokenOptions.AccessToken, - }, - } - pc.photonClient = photon.NewClient(pc.cfg.Global.CloudTarget, options, pc.logger) - } - - status, err := pc.photonClient.Status.Get() - if err != nil { - klog.Errorf("Photon Cloud Provider: new client creation failed. Error[%v]", err) - return nil, err - } - klog.V(2).Infof("Photon Cloud Provider: Status of the new photon controller client: %v", status) - - return pc.photonClient, nil -} - -func newPCCloud(cfg PCConfig) (*PCCloud, error) { - projID := cfg.Global.Project - vmID := cfg.Global.VMID - - // Get local hostname - hostname, err := os.Hostname() - if err != nil { - klog.Errorf("Photon Cloud Provider: get hostname failed. Error[%v]", err) - return nil, err - } - pc := PCCloud{ - cfg: &cfg, - localInstanceID: vmID, - localHostname: hostname, - localK8sHostname: "", - projID: projID, - } - - overrideIP = cfg.Global.OverrideIP - - return &pc, nil -} - -// Initialize passes a Kubernetes clientBuilder interface to the cloud provider -func (pc *PCCloud) Initialize(clientBuilder cloudprovider.ControllerClientBuilder, stop <-chan struct{}) { -} - -// Instances returns an implementation of Instances for Photon Controller. -func (pc *PCCloud) Instances() (cloudprovider.Instances, bool) { - return pc, true -} - -// List is an implementation of Instances.List. -func (pc *PCCloud) List(filter string) ([]k8stypes.NodeName, error) { - return nil, nil -} - -// NodeAddresses is an implementation of Instances.NodeAddresses. -func (pc *PCCloud) NodeAddresses(ctx context.Context, nodeName k8stypes.NodeName) ([]v1.NodeAddress, error) { - nodeAddrs := []v1.NodeAddress{} - name := string(nodeName) - - if name == pc.localK8sHostname { - ifaces, err := net.Interfaces() - if err != nil { - klog.Errorf("Photon Cloud Provider: net.Interfaces() failed for NodeAddresses. Error[%v]", err) - return nodeAddrs, err - } - - for _, i := range ifaces { - addrs, err := i.Addrs() - if err != nil { - klog.Warningf("Photon Cloud Provider: Failed to extract addresses for NodeAddresses. Error[%v]", err) - } else { - for _, addr := range addrs { - if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { - if ipnet.IP.To4() != nil { - // Filter external IP by MAC address OUIs from vCenter and from ESX - if strings.HasPrefix(i.HardwareAddr.String(), MAC_OUI_VC) || - strings.HasPrefix(i.HardwareAddr.String(), MAC_OUI_ESX) { - nodehelpers.AddToNodeAddresses(&nodeAddrs, - v1.NodeAddress{ - Type: v1.NodeExternalIP, - Address: ipnet.IP.String(), - }, - ) - } else { - nodehelpers.AddToNodeAddresses(&nodeAddrs, - v1.NodeAddress{ - Type: v1.NodeInternalIP, - Address: ipnet.IP.String(), - }, - ) - } - } - } - } - } - } - return nodeAddrs, nil - } - - // Inquiring IP addresses from photon controller endpoint only for a node other than this node. - // This is assumed to be done by master only. - vmID, err := getInstanceID(pc, name) - if err != nil { - klog.Errorf("Photon Cloud Provider: getInstanceID failed for NodeAddresses. Error[%v]", err) - return nodeAddrs, err - } - - photonClient, err := getPhotonClient(pc) - if err != nil { - klog.Errorf("Photon Cloud Provider: Failed to get photon client for NodeAddresses, error: [%v]", err) - return nodeAddrs, err - } - - // Retrieve the Photon VM's IP addresses from the Photon Controller endpoint based on the VM ID - vmList, err := photonClient.Projects.GetVMs(pc.projID, nil) - if err != nil { - klog.Errorf("Photon Cloud Provider: Failed to GetVMs for project %s. Error[%v]", pc.projID, err) - return nodeAddrs, err - } - - for _, vm := range vmList.Items { - if vm.ID == vmID { - task, err := photonClient.VMs.GetNetworks(vm.ID) - if err != nil { - klog.Errorf("Photon Cloud Provider: GetNetworks failed for node %s with vm.ID %s. Error[%v]", name, vm.ID, err) - return nodeAddrs, err - } else { - task, err = photonClient.Tasks.Wait(task.ID) - if err != nil { - klog.Errorf("Photon Cloud Provider: Wait task for GetNetworks failed for node %s with vm.ID %s. Error[%v]", name, vm.ID, err) - return nodeAddrs, err - } else { - networkConnections := task.ResourceProperties.(map[string]interface{}) - networks := networkConnections["networkConnections"].([]interface{}) - for _, nt := range networks { - ipAddr := "-" - macAddr := "-" - network := nt.(map[string]interface{}) - if val, ok := network["ipAddress"]; ok && val != nil { - ipAddr = val.(string) - } - if val, ok := network["macAddress"]; ok && val != nil { - macAddr = val.(string) - } - if ipAddr != "-" { - if strings.HasPrefix(macAddr, MAC_OUI_VC) || - strings.HasPrefix(macAddr, MAC_OUI_ESX) { - nodehelpers.AddToNodeAddresses(&nodeAddrs, - v1.NodeAddress{ - Type: v1.NodeExternalIP, - Address: ipAddr, - }, - ) - } else { - nodehelpers.AddToNodeAddresses(&nodeAddrs, - v1.NodeAddress{ - Type: v1.NodeInternalIP, - Address: ipAddr, - }, - ) - } - } - } - return nodeAddrs, nil - } - } - } - } - - klog.Errorf("Failed to find the node %s from Photon Controller endpoint", name) - return nodeAddrs, fmt.Errorf("Failed to find the node %s from Photon Controller endpoint", name) -} - -// NodeAddressesByProviderID returns the node addresses of an instances with the specified unique providerID -// This method will not be called from the node that is requesting this ID. i.e. metadata service -// and other local methods cannot be used here -func (pc *PCCloud) NodeAddressesByProviderID(ctx context.Context, providerID string) ([]v1.NodeAddress, error) { - return []v1.NodeAddress{}, cloudprovider.NotImplemented -} - -func (pc *PCCloud) AddSSHKeyToAllInstances(ctx context.Context, user string, keyData []byte) error { - return cloudprovider.NotImplemented -} - -func (pc *PCCloud) CurrentNodeName(ctx context.Context, hostname string) (k8stypes.NodeName, error) { - pc.localK8sHostname = hostname - return k8stypes.NodeName(hostname), nil -} - -func getInstanceID(pc *PCCloud, name string) (string, error) { - var vmID string - var err error - - if overrideIP == true { - vmID, err = getVMIDbyIP(pc, name) - } else { - vmID, err = getVMIDbyNodename(pc, name) - } - if err != nil { - return "", err - } - - if vmID == "" { - err = cloudprovider.InstanceNotFound - } - - return vmID, err -} - -// InstanceExistsByProviderID returns true if the instance with the given provider id still exists and is running. -// If false is returned with no error, the instance will be immediately deleted by the cloud controller manager. -func (pc *PCCloud) InstanceExistsByProviderID(ctx context.Context, providerID string) (bool, error) { - return false, cloudprovider.NotImplemented -} - -// InstanceShutdownByProviderID returns true if the instance is in safe state to detach volumes -func (pc *PCCloud) InstanceShutdownByProviderID(ctx context.Context, providerID string) (bool, error) { - return false, cloudprovider.NotImplemented -} - -// InstanceID returns the cloud provider ID of the specified instance. -func (pc *PCCloud) InstanceID(ctx context.Context, nodeName k8stypes.NodeName) (string, error) { - name := string(nodeName) - if name == pc.localK8sHostname { - return pc.localInstanceID, nil - } - // We assume only master need to get InstanceID of a node other than itself - id, err := getInstanceID(pc, name) - if err != nil { - klog.Errorf("Photon Cloud Provider: getInstanceID failed for InstanceID. Error[%v]", err) - } - return id, err - -} - -// InstanceTypeByProviderID returns the cloudprovider instance type of the node with the specified unique providerID -// This method will not be called from the node that is requesting this ID. i.e. metadata service -// and other local methods cannot be used here -func (pc *PCCloud) InstanceTypeByProviderID(ctx context.Context, providerID string) (string, error) { - return "", cloudprovider.NotImplemented -} - -func (pc *PCCloud) InstanceType(ctx context.Context, nodeName k8stypes.NodeName) (string, error) { - return "", nil -} - -func (pc *PCCloud) Clusters() (cloudprovider.Clusters, bool) { - return nil, true -} - -// ProviderName returns the cloud provider ID. -func (pc *PCCloud) ProviderName() string { - return ProviderName -} - -// LoadBalancer returns an implementation of LoadBalancer for Photon Controller. -func (pc *PCCloud) LoadBalancer() (cloudprovider.LoadBalancer, bool) { - return nil, false -} - -// Zones returns an implementation of Zones for Photon Controller. -func (pc *PCCloud) Zones() (cloudprovider.Zones, bool) { - return pc, true -} - -func (pc *PCCloud) GetZone(ctx context.Context) (cloudprovider.Zone, error) { - return pc.Zone, nil -} - -// GetZoneByProviderID implements Zones.GetZoneByProviderID -// This is particularly useful in external cloud providers where the kubelet -// does not initialize node data. -func (pc *PCCloud) GetZoneByProviderID(ctx context.Context, providerID string) (cloudprovider.Zone, error) { - return cloudprovider.Zone{}, errors.New("GetZoneByProviderID not implemented") -} - -// GetZoneByNodeName implements Zones.GetZoneByNodeName -// This is particularly useful in external cloud providers where the kubelet -// does not initialize node data. -func (pc *PCCloud) GetZoneByNodeName(ctx context.Context, nodeName k8stypes.NodeName) (cloudprovider.Zone, error) { - return cloudprovider.Zone{}, errors.New("GetZoneByNodeName not imeplemented") -} - -// Routes returns a false since the interface is not supported for photon controller. -func (pc *PCCloud) Routes() (cloudprovider.Routes, bool) { - return nil, false -} - -// HasClusterID returns true if the cluster has a clusterID -func (pc *PCCloud) HasClusterID() bool { - return true -} - -// AttachDisk attaches given virtual disk volume to the compute running kubelet. -func (pc *PCCloud) AttachDisk(ctx context.Context, pdID string, nodeName k8stypes.NodeName) error { - photonClient, err := getPhotonClient(pc) - if err != nil { - klog.Errorf("Photon Cloud Provider: Failed to get photon client for AttachDisk, error: [%v]", err) - return err - } - - operation := &photon.VmDiskOperation{ - DiskID: pdID, - } - - vmID, err := pc.InstanceID(ctx, nodeName) - if err != nil { - klog.Errorf("Photon Cloud Provider: pc.InstanceID failed for AttachDisk. Error[%v]", err) - return err - } - - task, err := photonClient.VMs.AttachDisk(vmID, operation) - if err != nil { - klog.Errorf("Photon Cloud Provider: Failed to attach disk with pdID %s. Error[%v]", pdID, err) - return err - } - - _, err = photonClient.Tasks.Wait(task.ID) - if err != nil { - klog.Errorf("Photon Cloud Provider: Failed to wait for task to attach disk with pdID %s. Error[%v]", pdID, err) - return err - } - - return nil -} - -// Detaches given virtual disk volume from the compute running kubelet. -func (pc *PCCloud) DetachDisk(ctx context.Context, pdID string, nodeName k8stypes.NodeName) error { - photonClient, err := getPhotonClient(pc) - if err != nil { - klog.Errorf("Photon Cloud Provider: Failed to get photon client for DetachDisk, error: [%v]", err) - return err - } - - operation := &photon.VmDiskOperation{ - DiskID: pdID, - } - - vmID, err := pc.InstanceID(ctx, nodeName) - if err != nil { - klog.Errorf("Photon Cloud Provider: pc.InstanceID failed for DetachDisk. Error[%v]", err) - return err - } - - task, err := photonClient.VMs.DetachDisk(vmID, operation) - if err != nil { - klog.Errorf("Photon Cloud Provider: Failed to detach disk with pdID %s. Error[%v]", pdID, err) - return err - } - - _, err = photonClient.Tasks.Wait(task.ID) - if err != nil { - klog.Errorf("Photon Cloud Provider: Failed to wait for task to detach disk with pdID %s. Error[%v]", pdID, err) - return err - } - - return nil -} - -// DiskIsAttached returns if disk is attached to the VM using controllers supported by the plugin. -func (pc *PCCloud) DiskIsAttached(ctx context.Context, pdID string, nodeName k8stypes.NodeName) (bool, error) { - photonClient, err := getPhotonClient(pc) - if err != nil { - klog.Errorf("Photon Cloud Provider: Failed to get photon client for DiskIsAttached, error: [%v]", err) - return false, err - } - - disk, err := photonClient.Disks.Get(pdID) - if err != nil { - klog.Errorf("Photon Cloud Provider: Failed to Get disk with pdID %s. Error[%v]", pdID, err) - return false, err - } - - vmID, err := pc.InstanceID(ctx, nodeName) - if err == cloudprovider.InstanceNotFound { - klog.Infof("Instance %q does not exist, disk %s will be detached automatically.", nodeName, pdID) - return false, nil - } - if err != nil { - klog.Errorf("Photon Cloud Provider: pc.InstanceID failed for DiskIsAttached. Error[%v]", err) - return false, err - } - - for _, vm := range disk.VMs { - if vm == vmID { - return true, nil - } - } - - return false, nil -} - -// DisksAreAttached returns if disks are attached to the VM using controllers supported by the plugin. -func (pc *PCCloud) DisksAreAttached(ctx context.Context, pdIDs []string, nodeName k8stypes.NodeName) (map[string]bool, error) { - attached := make(map[string]bool) - photonClient, err := getPhotonClient(pc) - if err != nil { - klog.Errorf("Photon Cloud Provider: Failed to get photon client for DisksAreAttached, error: [%v]", err) - return attached, err - } - - for _, pdID := range pdIDs { - attached[pdID] = false - } - - vmID, err := pc.InstanceID(ctx, nodeName) - if err == cloudprovider.InstanceNotFound { - klog.Infof("Instance %q does not exist, its disks will be detached automatically.", nodeName) - // make all the disks as detached. - return attached, nil - } - if err != nil { - klog.Errorf("Photon Cloud Provider: pc.InstanceID failed for DiskIsAttached. Error[%v]", err) - return attached, err - } - - for _, pdID := range pdIDs { - disk, err := photonClient.Disks.Get(pdID) - if err != nil { - klog.Warningf("Photon Cloud Provider: failed to get VMs for persistent disk %s, err [%v]", pdID, err) - } else { - for _, vm := range disk.VMs { - if vm == vmID { - attached[pdID] = true - } - } - } - } - - return attached, nil -} - -// Create a volume of given size (in GB). -func (pc *PCCloud) CreateDisk(volumeOptions *VolumeOptions) (pdID string, err error) { - photonClient, err := getPhotonClient(pc) - if err != nil { - klog.Errorf("Photon Cloud Provider: Failed to get photon client for CreateDisk, error: [%v]", err) - return "", err - } - - diskSpec := photon.DiskCreateSpec{} - diskSpec.Name = volumeOptions.Name - diskSpec.Flavor = volumeOptions.Flavor - diskSpec.CapacityGB = volumeOptions.CapacityGB - diskSpec.Kind = DiskSpecKind - - task, err := photonClient.Projects.CreateDisk(pc.projID, &diskSpec) - if err != nil { - klog.Errorf("Photon Cloud Provider: Failed to CreateDisk. Error[%v]", err) - return "", err - } - - waitTask, err := photonClient.Tasks.Wait(task.ID) - if err != nil { - klog.Errorf("Photon Cloud Provider: Failed to wait for task to CreateDisk. Error[%v]", err) - return "", err - } - - return waitTask.Entity.ID, nil -} - -// DeleteDisk deletes a volume given volume name. -func (pc *PCCloud) DeleteDisk(pdID string) error { - photonClient, err := getPhotonClient(pc) - if err != nil { - klog.Errorf("Photon Cloud Provider: Failed to get photon client for DeleteDisk, error: [%v]", err) - return err - } - - task, err := photonClient.Disks.Delete(pdID) - if err != nil { - klog.Errorf("Photon Cloud Provider: Failed to DeleteDisk. Error[%v]", err) - return err - } - - _, err = photonClient.Tasks.Wait(task.ID) - if err != nil { - klog.Errorf("Photon Cloud Provider: Failed to wait for task to DeleteDisk. Error[%v]", err) - return err - } - - return nil -} diff --git a/pkg/cloudprovider/providers/photon/photon_test.go b/pkg/cloudprovider/providers/photon/photon_test.go deleted file mode 100644 index 235eab11bec..00000000000 --- a/pkg/cloudprovider/providers/photon/photon_test.go +++ /dev/null @@ -1,202 +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 photon - -import ( - "context" - "log" - "os" - "strconv" - "strings" - "testing" - - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/rand" - cloudprovider "k8s.io/cloud-provider" -) - -func configFromEnv() (TestVM string, TestFlavor string, cfg PCConfig, ok bool) { - var AuthEnabled bool - var OverrideIP bool - var err error - cfg.Global.CloudTarget = os.Getenv("PHOTON_TARGET") - cfg.Global.Project = os.Getenv("PHOTON_PROJECT") - cfg.Global.VMID = os.Getenv("PHOTON_VMID") - if os.Getenv("PHOTON_AUTH_ENABLED") != "" { - AuthEnabled, err = strconv.ParseBool(os.Getenv("PHOTON_AUTH_ENABLED")) - } else { - AuthEnabled = false - } - if err != nil { - log.Fatal(err) - } - cfg.Global.AuthEnabled = AuthEnabled - if os.Getenv("PHOTON_OVERRIDE_IP") != "" { - OverrideIP, err = strconv.ParseBool(os.Getenv("PHOTON_OVERRIDE_IP")) - } else { - OverrideIP = false - } - if err != nil { - log.Fatal(err) - } - cfg.Global.OverrideIP = OverrideIP - - TestVM = os.Getenv("PHOTON_TEST_VM") - if os.Getenv("PHOTON_TEST_FLAVOR") != "" { - TestFlavor = os.Getenv("PHOTON_TEST_FLAVOR") - } else { - TestFlavor = "" - } - if err != nil { - log.Fatal(err) - } - - ok = (cfg.Global.CloudTarget != "" && - cfg.Global.Project != "" && - cfg.Global.VMID != "" && - TestVM != "") - - return -} - -func TestReadConfig(t *testing.T) { - _, err := readConfig(nil) - if err == nil { - t.Errorf("Should fail when no config is provided: %s", err) - } - - cfg, err := readConfig(strings.NewReader(` -[Global] -target = 0.0.0.0 -project = project -overrideIP = true -vmID = vmid -authentication = false -`)) - if err != nil { - t.Fatalf("Should succeed when a valid config is provided: %s", err) - } - - if cfg.Global.CloudTarget != "0.0.0.0" { - t.Errorf("incorrect photon target ip: %s", cfg.Global.CloudTarget) - } - - if cfg.Global.Project != "project" { - t.Errorf("incorrect project: %s", cfg.Global.Project) - } - - if cfg.Global.VMID != "vmid" { - t.Errorf("incorrect vmid: %s", cfg.Global.VMID) - } -} - -func TestNewPCCloud(t *testing.T) { - _, _, cfg, ok := configFromEnv() - if !ok { - t.Skipf("No config found in environment") - } - - _, err := newPCCloud(cfg) - if err != nil { - t.Fatalf("Failed to create new Photon client: %s", err) - } -} - -func TestInstances(t *testing.T) { - testVM, _, cfg, ok := configFromEnv() - if !ok { - t.Skipf("No config found in environment") - } - NodeName := types.NodeName(testVM) - - pc, err := newPCCloud(cfg) - if err != nil { - t.Fatalf("Failed to create new Photon client: %s", err) - } - - i, ok := pc.Instances() - if !ok { - t.Fatalf("Instances() returned false") - } - - nonExistingVM := types.NodeName(rand.String(15)) - instanceId, err := i.InstanceID(context.TODO(), NodeName) - if err != nil { - t.Fatalf("Instances.InstanceID(%s) failed: %s", testVM, err) - } - t.Logf("Found InstanceID(%s) = %s\n", testVM, instanceId) - - _, err = i.InstanceID(context.TODO(), nonExistingVM) - if err == cloudprovider.InstanceNotFound { - t.Logf("VM %s was not found as expected\n", nonExistingVM) - } else if err == nil { - t.Fatalf("Instances.InstanceID did not fail as expected, VM %s was found", nonExistingVM) - } else { - t.Fatalf("Instances.InstanceID did not fail as expected, err: %v", err) - } - - addrs, err := i.NodeAddresses(context.TODO(), NodeName) - if err != nil { - t.Fatalf("Instances.NodeAddresses(%s) failed: %s", testVM, err) - } - t.Logf("Found NodeAddresses(%s) = %s\n", testVM, addrs) -} - -func TestVolumes(t *testing.T) { - testVM, testFlavor, cfg, ok := configFromEnv() - if !ok { - t.Skipf("No config found in environment") - } - - pc, err := newPCCloud(cfg) - if err != nil { - t.Fatalf("Failed to create new Photon client: %s", err) - } - - NodeName := types.NodeName(testVM) - - volumeOptions := &VolumeOptions{ - CapacityGB: 2, - Tags: nil, - Name: "kubernetes-test-volume-" + rand.String(10), - Flavor: testFlavor} - - pdID, err := pc.CreateDisk(volumeOptions) - if err != nil { - t.Fatalf("Cannot create a Photon persistent disk: %v", err) - } - - err = pc.AttachDisk(context.TODO(), pdID, NodeName) - if err != nil { - t.Fatalf("Cannot attach persistent disk(%s) to VM(%s): %v", pdID, testVM, err) - } - - _, err = pc.DiskIsAttached(context.TODO(), pdID, NodeName) - if err != nil { - t.Fatalf("Cannot attach persistent disk(%s) to VM(%s): %v", pdID, testVM, err) - } - - err = pc.DetachDisk(context.TODO(), pdID, NodeName) - if err != nil { - t.Fatalf("Cannot detach persisten disk(%s) from VM(%s): %v", pdID, testVM, err) - } - - err = pc.DeleteDisk(pdID) - if err != nil { - t.Fatalf("Cannot delete persisten disk(%s): %v", pdID, err) - } -} diff --git a/pkg/cloudprovider/providers/providers.go b/pkg/cloudprovider/providers/providers.go index a93663bf52b..8a5c7c9e815 100644 --- a/pkg/cloudprovider/providers/providers.go +++ b/pkg/cloudprovider/providers/providers.go @@ -18,10 +18,7 @@ package cloudprovider import ( // Cloud providers - _ "k8s.io/kubernetes/pkg/cloudprovider/providers/cloudstack" _ "k8s.io/kubernetes/pkg/cloudprovider/providers/openstack" - _ "k8s.io/kubernetes/pkg/cloudprovider/providers/ovirt" - _ "k8s.io/kubernetes/pkg/cloudprovider/providers/photon" _ "k8s.io/legacy-cloud-providers/aws" _ "k8s.io/legacy-cloud-providers/azure" _ "k8s.io/legacy-cloud-providers/gce" diff --git a/pkg/volume/BUILD b/pkg/volume/BUILD index c5a9f8dda31..3e4c112b507 100644 --- a/pkg/volume/BUILD +++ b/pkg/volume/BUILD @@ -94,7 +94,6 @@ filegroup( "//pkg/volume/iscsi:all-srcs", "//pkg/volume/local:all-srcs", "//pkg/volume/nfs:all-srcs", - "//pkg/volume/photon_pd:all-srcs", "//pkg/volume/portworx:all-srcs", "//pkg/volume/projected:all-srcs", "//pkg/volume/quobyte:all-srcs", diff --git a/pkg/volume/photon_pd/BUILD b/pkg/volume/photon_pd/BUILD deleted file mode 100644 index 4f5ddfbde1a..00000000000 --- a/pkg/volume/photon_pd/BUILD +++ /dev/null @@ -1,63 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", - "go_test", -) - -go_library( - name = "go_default_library", - srcs = [ - "attacher.go", - "photon_pd.go", - "photon_util.go", - ], - importpath = "k8s.io/kubernetes/pkg/volume/photon_pd", - deps = [ - "//pkg/cloudprovider/providers/photon:go_default_library", - "//pkg/util/mount:go_default_library", - "//pkg/volume:go_default_library", - "//pkg/volume/util:go_default_library", - "//staging/src/k8s.io/api/core/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", - "//staging/src/k8s.io/cloud-provider:go_default_library", - "//staging/src/k8s.io/cloud-provider/volume/helpers:go_default_library", - "//vendor/k8s.io/klog:go_default_library", - "//vendor/k8s.io/utils/strings:go_default_library", - ], -) - -go_test( - name = "go_default_test", - srcs = [ - "attacher_test.go", - "photon_pd_test.go", - ], - embed = [":go_default_library"], - deps = [ - "//pkg/cloudprovider/providers/photon:go_default_library", - "//pkg/util/mount:go_default_library", - "//pkg/volume:go_default_library", - "//pkg/volume/testing:go_default_library", - "//staging/src/k8s.io/api/core/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", - "//staging/src/k8s.io/client-go/util/testing:go_default_library", - "//vendor/k8s.io/klog:go_default_library", - ], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [":package-srcs"], - tags = ["automanaged"], -) diff --git a/pkg/volume/photon_pd/OWNERS b/pkg/volume/photon_pd/OWNERS deleted file mode 100644 index 36c201edd9a..00000000000 --- a/pkg/volume/photon_pd/OWNERS +++ /dev/null @@ -1,6 +0,0 @@ -# See the OWNERS docs at https://go.k8s.io/owners - -maintainers: -- luomiao -- kerneltime -- abrarshivani diff --git a/pkg/volume/photon_pd/attacher.go b/pkg/volume/photon_pd/attacher.go deleted file mode 100644 index 60ab3fa5cc7..00000000000 --- a/pkg/volume/photon_pd/attacher.go +++ /dev/null @@ -1,317 +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 photon_pd - -import ( - "context" - "fmt" - "os" - "path" - "path/filepath" - "strings" - "time" - - "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/klog" - "k8s.io/kubernetes/pkg/cloudprovider/providers/photon" - "k8s.io/kubernetes/pkg/util/mount" - "k8s.io/kubernetes/pkg/volume" - volumeutil "k8s.io/kubernetes/pkg/volume/util" -) - -type photonPersistentDiskAttacher struct { - host volume.VolumeHost - photonDisks photon.Disks -} - -var _ volume.Attacher = &photonPersistentDiskAttacher{} - -var _ volume.DeviceMounter = &photonPersistentDiskAttacher{} - -var _ volume.AttachableVolumePlugin = &photonPersistentDiskPlugin{} - -var _ volume.DeviceMountableVolumePlugin = &photonPersistentDiskPlugin{} - -func (plugin *photonPersistentDiskPlugin) NewAttacher() (volume.Attacher, error) { - photonCloud, err := getCloudProvider(plugin.host.GetCloudProvider()) - if err != nil { - klog.Errorf("Photon Controller attacher: NewAttacher failed to get cloud provider") - return nil, err - } - - return &photonPersistentDiskAttacher{ - host: plugin.host, - photonDisks: photonCloud, - }, nil -} - -func (plugin *photonPersistentDiskPlugin) NewDeviceMounter() (volume.DeviceMounter, error) { - return plugin.NewAttacher() -} - -// Attaches the volume specified by the given spec to the given host. -// On success, returns the device path where the device was attached on the -// node. -// Callers are responsible for retryinging on failure. -// Callers are responsible for thread safety between concurrent attach and -// detach operations. -func (attacher *photonPersistentDiskAttacher) Attach(spec *volume.Spec, nodeName types.NodeName) (string, error) { - hostName := string(nodeName) - volumeSource, _, err := getVolumeSource(spec) - if err != nil { - klog.Errorf("Photon Controller attacher: Attach failed to get volume source") - return "", err - } - attached, err := attacher.photonDisks.DiskIsAttached(context.TODO(), volumeSource.PdID, nodeName) - - if err != nil { - klog.Warningf("Photon Controller: couldn't check if disk is Attached for host %s, will try attach disk: %+v", hostName, err) - attached = false - } - - if !attached { - klog.V(4).Infof("Photon Controller: Attach disk called for host %s", hostName) - - err = attacher.photonDisks.AttachDisk(context.TODO(), volumeSource.PdID, nodeName) - if err != nil { - klog.Errorf("Error attaching volume %q to node %q: %+v", volumeSource.PdID, nodeName, err) - return "", err - } - } - - PdidWithNoHypens := strings.Replace(volumeSource.PdID, "-", "", -1) - return filepath.Join(diskByIDPath, diskPhotonPrefix+PdidWithNoHypens), nil -} - -func (attacher *photonPersistentDiskAttacher) VolumesAreAttached(specs []*volume.Spec, nodeName types.NodeName) (map[*volume.Spec]bool, error) { - volumesAttachedCheck := make(map[*volume.Spec]bool) - volumeSpecMap := make(map[string]*volume.Spec) - pdIDList := []string{} - for _, spec := range specs { - volumeSource, _, err := getVolumeSource(spec) - if err != nil { - klog.Errorf("Error getting volume (%q) source : %v", spec.Name(), err) - continue - } - - pdIDList = append(pdIDList, volumeSource.PdID) - volumesAttachedCheck[spec] = true - volumeSpecMap[volumeSource.PdID] = spec - } - attachedResult, err := attacher.photonDisks.DisksAreAttached(context.TODO(), pdIDList, nodeName) - if err != nil { - klog.Errorf( - "Error checking if volumes (%v) are attached to current node (%q). err=%v", - pdIDList, nodeName, err) - return volumesAttachedCheck, err - } - - for pdID, attached := range attachedResult { - if !attached { - spec := volumeSpecMap[pdID] - volumesAttachedCheck[spec] = false - klog.V(2).Infof("VolumesAreAttached: check volume %q (specName: %q) is no longer attached", pdID, spec.Name()) - } - } - return volumesAttachedCheck, nil -} - -func (attacher *photonPersistentDiskAttacher) WaitForAttach(spec *volume.Spec, devicePath string, _ *v1.Pod, timeout time.Duration) (string, error) { - volumeSource, _, err := getVolumeSource(spec) - if err != nil { - klog.Errorf("Photon Controller attacher: WaitForAttach failed to get volume source") - return "", err - } - - if devicePath == "" { - return "", fmt.Errorf("WaitForAttach failed for PD %s: devicePath is empty.", volumeSource.PdID) - } - - // scan scsi path to discover the new disk - scsiHostScan() - - ticker := time.NewTicker(checkSleepDuration) - defer ticker.Stop() - - timer := time.NewTimer(timeout) - defer timer.Stop() - - for { - select { - case <-ticker.C: - klog.V(4).Infof("Checking PD %s is attached", volumeSource.PdID) - checkPath, err := verifyDevicePath(devicePath) - if err != nil { - // Log error, if any, and continue checking periodically. See issue #11321 - klog.Warningf("Photon Controller attacher: WaitForAttach with devicePath %s Checking PD %s Error verify path", devicePath, volumeSource.PdID) - } else if checkPath != "" { - // A device path has successfully been created for the VMDK - klog.V(4).Infof("Successfully found attached PD %s.", volumeSource.PdID) - // map path with spec.Name() - volName := spec.Name() - realPath, _ := filepath.EvalSymlinks(devicePath) - deviceName := path.Base(realPath) - volNameToDeviceName[volName] = deviceName - return devicePath, nil - } - case <-timer.C: - return "", fmt.Errorf("Could not find attached PD %s. Timeout waiting for mount paths to be created.", volumeSource.PdID) - } - } -} - -// GetDeviceMountPath returns a path where the device should -// point which should be bind mounted for individual volumes. -func (attacher *photonPersistentDiskAttacher) GetDeviceMountPath(spec *volume.Spec) (string, error) { - volumeSource, _, err := getVolumeSource(spec) - if err != nil { - klog.Errorf("Photon Controller attacher: GetDeviceMountPath failed to get volume source") - return "", err - } - - return makeGlobalPDPath(attacher.host, volumeSource.PdID), nil -} - -// GetMountDeviceRefs finds all other references to the device referenced -// by deviceMountPath; returns a list of paths. -func (plugin *photonPersistentDiskPlugin) GetDeviceMountRefs(deviceMountPath string) ([]string, error) { - mounter := plugin.host.GetMounter(plugin.GetPluginName()) - return mounter.GetMountRefs(deviceMountPath) -} - -// MountDevice mounts device to global mount point. -func (attacher *photonPersistentDiskAttacher) MountDevice(spec *volume.Spec, devicePath string, deviceMountPath string) error { - mounter := attacher.host.GetMounter(photonPersistentDiskPluginName) - notMnt, err := mounter.IsLikelyNotMountPoint(deviceMountPath) - if err != nil { - if os.IsNotExist(err) { - if err := os.MkdirAll(deviceMountPath, 0750); err != nil { - klog.Errorf("Failed to create directory at %#v. err: %s", deviceMountPath, err) - return err - } - notMnt = true - } else { - return err - } - } - - volumeSource, _, err := getVolumeSource(spec) - if err != nil { - klog.Errorf("Photon Controller attacher: MountDevice failed to get volume source. err: %s", err) - return err - } - - options := []string{} - - if notMnt { - diskMounter := volumeutil.NewSafeFormatAndMountFromHost(photonPersistentDiskPluginName, attacher.host) - mountOptions := volumeutil.MountOptionFromSpec(spec) - err = diskMounter.FormatAndMount(devicePath, deviceMountPath, volumeSource.FSType, mountOptions) - if err != nil { - os.Remove(deviceMountPath) - return err - } - klog.V(4).Infof("formatting spec %v devicePath %v deviceMountPath %v fs %v with options %+v", spec.Name(), devicePath, deviceMountPath, volumeSource.FSType, options) - } - return nil -} - -type photonPersistentDiskDetacher struct { - mounter mount.Interface - photonDisks photon.Disks -} - -var _ volume.Detacher = &photonPersistentDiskDetacher{} - -var _ volume.DeviceUnmounter = &photonPersistentDiskDetacher{} - -func (plugin *photonPersistentDiskPlugin) NewDetacher() (volume.Detacher, error) { - photonCloud, err := getCloudProvider(plugin.host.GetCloudProvider()) - if err != nil { - klog.Errorf("Photon Controller attacher: NewDetacher failed to get cloud provider. err: %s", err) - return nil, err - } - - return &photonPersistentDiskDetacher{ - mounter: plugin.host.GetMounter(plugin.GetPluginName()), - photonDisks: photonCloud, - }, nil -} - -func (plugin *photonPersistentDiskPlugin) NewDeviceUnmounter() (volume.DeviceUnmounter, error) { - return plugin.NewDetacher() -} - -// Detach the given device from the given host. -func (detacher *photonPersistentDiskDetacher) Detach(volumeName string, nodeName types.NodeName) error { - - hostName := string(nodeName) - pdID := volumeName - attached, err := detacher.photonDisks.DiskIsAttached(context.TODO(), pdID, nodeName) - if err != nil { - // Log error and continue with detach - klog.Errorf( - "Error checking if persistent disk (%q) is already attached to current node (%q). Will continue and try detach anyway. err=%v", - pdID, hostName, err) - } - - if err == nil && !attached { - // Volume is already detached from node. - klog.V(4).Infof("detach operation was successful. persistent disk %q is already detached from node %q.", pdID, hostName) - return nil - } - - if err := detacher.photonDisks.DetachDisk(context.TODO(), pdID, nodeName); err != nil { - klog.Errorf("Error detaching volume %q: %v", pdID, err) - return err - } - return nil -} - -func (detacher *photonPersistentDiskDetacher) WaitForDetach(devicePath string, timeout time.Duration) error { - ticker := time.NewTicker(checkSleepDuration) - defer ticker.Stop() - timer := time.NewTimer(timeout) - defer timer.Stop() - - for { - select { - case <-ticker.C: - klog.V(4).Infof("Checking device %q is detached.", devicePath) - if pathExists, err := mount.PathExists(devicePath); err != nil { - return fmt.Errorf("Error checking if device path exists: %v", err) - } else if !pathExists { - return nil - } - case <-timer.C: - return fmt.Errorf("Timeout reached; Device %v is still attached", devicePath) - } - } -} - -func (detacher *photonPersistentDiskDetacher) UnmountDevice(deviceMountPath string) error { - return mount.CleanupMountPoint(deviceMountPath, detacher.mounter, false) -} - -func (plugin *photonPersistentDiskPlugin) CanAttach(spec *volume.Spec) (bool, error) { - return true, nil -} - -func (plugin *photonPersistentDiskPlugin) CanDeviceMount(spec *volume.Spec) (bool, error) { - return true, nil -} diff --git a/pkg/volume/photon_pd/attacher_test.go b/pkg/volume/photon_pd/attacher_test.go deleted file mode 100644 index 077131610b5..00000000000 --- a/pkg/volume/photon_pd/attacher_test.go +++ /dev/null @@ -1,330 +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 photon_pd - -import ( - "context" - "errors" - "testing" - - "k8s.io/api/core/v1" - "k8s.io/kubernetes/pkg/cloudprovider/providers/photon" - "k8s.io/kubernetes/pkg/volume" - volumetest "k8s.io/kubernetes/pkg/volume/testing" - - "k8s.io/apimachinery/pkg/types" - "k8s.io/klog" -) - -func TestGetDeviceName_Volume(t *testing.T) { - plugin := newPlugin() - name := "my-photon-volume" - spec := createVolSpec(name, false) - - deviceName, err := plugin.GetVolumeName(spec) - if err != nil { - t.Errorf("GetDeviceName error: %v", err) - } - if deviceName != name { - t.Errorf("GetDeviceName error: expected %s, got %s", name, deviceName) - } -} - -func TestGetDeviceName_PersistentVolume(t *testing.T) { - plugin := newPlugin() - name := "my-photon-pv" - spec := createPVSpec(name, true) - - deviceName, err := plugin.GetVolumeName(spec) - if err != nil { - t.Errorf("GetDeviceName error: %v", err) - } - if deviceName != name { - t.Errorf("GetDeviceName error: expected %s, got %s", name, deviceName) - } -} - -// One testcase for TestAttachDetach table test below -type testcase struct { - name string - // For fake Photon cloud provider: - attach attachCall - detach detachCall - diskIsAttached diskIsAttachedCall - t *testing.T - - // Actual test to run - test func(test *testcase) (string, error) - // Expected return of the test - expectedDevice string - expectedError error -} - -func TestAttachDetach(t *testing.T) { - diskName := "000-000-000" - nodeName := types.NodeName("instance") - readOnly := false - spec := createVolSpec(diskName, readOnly) - detachError := errors.New("Fake detach error") - diskCheckError := errors.New("Fake DiskIsAttached error") - tests := []testcase{ - // Successful Attach call - { - name: "Attach_Positive", - diskIsAttached: diskIsAttachedCall{diskName, nodeName, false, diskCheckError}, - attach: attachCall{diskName, nodeName, nil}, - test: func(testcase *testcase) (string, error) { - attacher := newAttacher(testcase) - return attacher.Attach(spec, nodeName) - }, - expectedDevice: "/dev/disk/by-id/wwn-0x000000000", - }, - - // Disk is already attached - { - name: "Attach_Positive_AlreadyAttached", - diskIsAttached: diskIsAttachedCall{diskName, nodeName, false, diskCheckError}, - attach: attachCall{diskName, nodeName, nil}, - test: func(testcase *testcase) (string, error) { - attacher := newAttacher(testcase) - return attacher.Attach(spec, nodeName) - }, - expectedDevice: "/dev/disk/by-id/wwn-0x000000000", - }, - - // Detach succeeds - { - name: "Detach_Positive", - diskIsAttached: diskIsAttachedCall{diskName, nodeName, true, nil}, - detach: detachCall{diskName, nodeName, nil}, - test: func(testcase *testcase) (string, error) { - detacher := newDetacher(testcase) - return "", detacher.Detach(diskName, nodeName) - }, - }, - - // Disk is already detached - { - name: "Detach_Positive_AlreadyDetached", - diskIsAttached: diskIsAttachedCall{diskName, nodeName, false, nil}, - test: func(testcase *testcase) (string, error) { - detacher := newDetacher(testcase) - return "", detacher.Detach(diskName, nodeName) - }, - }, - - // Detach succeeds when DiskIsAttached fails - { - name: "Detach_Positive_CheckFails", - diskIsAttached: diskIsAttachedCall{diskName, nodeName, false, diskCheckError}, - detach: detachCall{diskName, nodeName, nil}, - test: func(testcase *testcase) (string, error) { - detacher := newDetacher(testcase) - return "", detacher.Detach(diskName, nodeName) - }, - }, - - // Detach fails - { - name: "Detach_Negative", - diskIsAttached: diskIsAttachedCall{diskName, nodeName, false, diskCheckError}, - detach: detachCall{diskName, nodeName, detachError}, - test: func(testcase *testcase) (string, error) { - detacher := newDetacher(testcase) - return "", detacher.Detach(diskName, nodeName) - }, - expectedError: detachError, - }, - } - - for _, testcase := range tests { - testcase.t = t - device, err := testcase.test(&testcase) - if err != testcase.expectedError { - t.Errorf("%s failed: expected err=%q, got %q", testcase.name, testcase.expectedError.Error(), err.Error()) - } - if device != testcase.expectedDevice { - t.Errorf("%s failed: expected device=%q, got %q", testcase.name, testcase.expectedDevice, device) - } - t.Logf("Test %q succeeded", testcase.name) - } -} - -// newPlugin creates a new gcePersistentDiskPlugin with fake cloud, NewAttacher -// and NewDetacher won't work. -func newPlugin() *photonPersistentDiskPlugin { - host := volumetest.NewFakeVolumeHost("/tmp", nil, nil) - plugins := ProbeVolumePlugins() - plugin := plugins[0] - plugin.Init(host) - return plugin.(*photonPersistentDiskPlugin) -} - -func newAttacher(testcase *testcase) *photonPersistentDiskAttacher { - return &photonPersistentDiskAttacher{ - host: nil, - photonDisks: testcase, - } -} - -func newDetacher(testcase *testcase) *photonPersistentDiskDetacher { - return &photonPersistentDiskDetacher{ - photonDisks: testcase, - } -} - -func createVolSpec(name string, readOnly bool) *volume.Spec { - return &volume.Spec{ - Volume: &v1.Volume{ - VolumeSource: v1.VolumeSource{ - PhotonPersistentDisk: &v1.PhotonPersistentDiskVolumeSource{ - PdID: name, - }, - }, - }, - } -} - -func createPVSpec(name string, readOnly bool) *volume.Spec { - return &volume.Spec{ - PersistentVolume: &v1.PersistentVolume{ - Spec: v1.PersistentVolumeSpec{ - PersistentVolumeSource: v1.PersistentVolumeSource{ - PhotonPersistentDisk: &v1.PhotonPersistentDiskVolumeSource{ - PdID: name, - }, - }, - }, - }, - } -} - -// Fake PhotonPD implementation - -type attachCall struct { - diskName string - nodeName types.NodeName - ret error -} - -type detachCall struct { - diskName string - nodeName types.NodeName - ret error -} - -type diskIsAttachedCall struct { - diskName string - nodeName types.NodeName - isAttached bool - ret error -} - -func (testcase *testcase) AttachDisk(ctx context.Context, diskName string, nodeName types.NodeName) error { - expected := &testcase.attach - - if expected.diskName == "" && expected.nodeName == "" { - // testcase.attach looks uninitialized, test did not expect to call - // AttachDisk - testcase.t.Errorf("Unexpected AttachDisk call!") - return errors.New("Unexpected AttachDisk call!") - } - - if expected.diskName != diskName { - testcase.t.Errorf("Unexpected AttachDisk call: expected diskName %s, got %s", expected.diskName, diskName) - return errors.New("Unexpected AttachDisk call: wrong diskName") - } - - if expected.nodeName != nodeName { - testcase.t.Errorf("Unexpected AttachDisk call: expected nodeName %s, got %s", expected.nodeName, nodeName) - return errors.New("Unexpected AttachDisk call: wrong nodeName") - } - - klog.V(4).Infof("AttachDisk call: %s, %s, returning %v", diskName, nodeName, expected.ret) - - return expected.ret -} - -func (testcase *testcase) DetachDisk(ctx context.Context, diskName string, nodeName types.NodeName) error { - expected := &testcase.detach - - if expected.diskName == "" && expected.nodeName == "" { - // testcase.detach looks uninitialized, test did not expect to call - // DetachDisk - testcase.t.Errorf("Unexpected DetachDisk call!") - return errors.New("Unexpected DetachDisk call!") - } - - if expected.diskName != diskName { - testcase.t.Errorf("Unexpected DetachDisk call: expected diskName %s, got %s", expected.diskName, diskName) - return errors.New("Unexpected DetachDisk call: wrong diskName") - } - - if expected.nodeName != nodeName { - testcase.t.Errorf("Unexpected DetachDisk call: expected nodeName %s, got %s", expected.nodeName, nodeName) - return errors.New("Unexpected DetachDisk call: wrong nodeName") - } - - klog.V(4).Infof("DetachDisk call: %s, %s, returning %v", diskName, nodeName, expected.ret) - - return expected.ret -} - -func (testcase *testcase) DiskIsAttached(ctx context.Context, diskName string, nodeName types.NodeName) (bool, error) { - expected := &testcase.diskIsAttached - - if expected.diskName == "" && expected.nodeName == "" { - // testcase.diskIsAttached looks uninitialized, test did not expect to - // call DiskIsAttached - testcase.t.Errorf("Unexpected DiskIsAttached call!") - return false, errors.New("Unexpected DiskIsAttached call!") - } - - if expected.diskName != diskName { - testcase.t.Errorf("Unexpected DiskIsAttached call: expected diskName %s, got %s", expected.diskName, diskName) - return false, errors.New("Unexpected DiskIsAttached call: wrong diskName") - } - - if expected.nodeName != nodeName { - testcase.t.Errorf("Unexpected DiskIsAttached call: expected nodeName %s, got %s", expected.nodeName, nodeName) - return false, errors.New("Unexpected DiskIsAttached call: wrong nodeName") - } - - klog.V(4).Infof("DiskIsAttached call: %s, %s, returning %v, %v", diskName, nodeName, expected.isAttached, expected.ret) - - return expected.isAttached, expected.ret -} - -func (testcase *testcase) DisksAreAttached(ctx context.Context, diskNames []string, nodeName types.NodeName) (map[string]bool, error) { - return nil, errors.New("Not implemented") -} - -func (testcase *testcase) CreateDisk(volumeOptions *photon.VolumeOptions) (volumeName string, err error) { - return "", errors.New("Not implemented") -} - -func (testcase *testcase) DeleteDisk(volumeName string) error { - return errors.New("Not implemented") -} - -func (testcase *testcase) GetVolumeLabels(volumeName string) (map[string]string, error) { - return map[string]string{}, errors.New("Not implemented") -} - -func (testcase *testcase) GetDiskPath(volumeName string) (string, error) { - return "", errors.New("Not implemented") -} diff --git a/pkg/volume/photon_pd/photon_pd.go b/pkg/volume/photon_pd/photon_pd.go deleted file mode 100644 index 54fe82fa02b..00000000000 --- a/pkg/volume/photon_pd/photon_pd.go +++ /dev/null @@ -1,409 +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 photon_pd - -import ( - "fmt" - "os" - "path/filepath" - - "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/resource" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/klog" - "k8s.io/kubernetes/pkg/util/mount" - "k8s.io/kubernetes/pkg/volume" - "k8s.io/kubernetes/pkg/volume/util" - utilstrings "k8s.io/utils/strings" -) - -// This is the primary entrypoint for volume plugins. -func ProbeVolumePlugins() []volume.VolumePlugin { - return []volume.VolumePlugin{&photonPersistentDiskPlugin{}} -} - -type photonPersistentDiskPlugin struct { - host volume.VolumeHost -} - -var _ volume.VolumePlugin = &photonPersistentDiskPlugin{} -var _ volume.PersistentVolumePlugin = &photonPersistentDiskPlugin{} -var _ volume.DeletableVolumePlugin = &photonPersistentDiskPlugin{} -var _ volume.ProvisionableVolumePlugin = &photonPersistentDiskPlugin{} - -const ( - photonPersistentDiskPluginName = "kubernetes.io/photon-pd" -) - -func (plugin *photonPersistentDiskPlugin) Init(host volume.VolumeHost) error { - plugin.host = host - return nil -} - -func (plugin *photonPersistentDiskPlugin) GetPluginName() string { - return photonPersistentDiskPluginName -} - -func (plugin *photonPersistentDiskPlugin) GetVolumeName(spec *volume.Spec) (string, error) { - volumeSource, _, err := getVolumeSource(spec) - if err != nil { - klog.Errorf("Photon volume plugin: GetVolumeName failed to get volume source") - return "", err - } - - return volumeSource.PdID, nil -} - -func (plugin *photonPersistentDiskPlugin) CanSupport(spec *volume.Spec) bool { - return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.PhotonPersistentDisk != nil) || - (spec.Volume != nil && spec.Volume.PhotonPersistentDisk != nil) -} - -func (plugin *photonPersistentDiskPlugin) IsMigratedToCSI() bool { - return false -} - -func (plugin *photonPersistentDiskPlugin) RequiresRemount() bool { - return false -} - -func (plugin *photonPersistentDiskPlugin) SupportsMountOption() bool { - return true -} - -func (plugin *photonPersistentDiskPlugin) SupportsBulkVolumeVerification() bool { - return false -} - -func (plugin *photonPersistentDiskPlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.Mounter, error) { - return plugin.newMounterInternal(spec, pod.UID, &PhotonDiskUtil{}, plugin.host.GetMounter(plugin.GetPluginName())) -} - -func (plugin *photonPersistentDiskPlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) { - return plugin.newUnmounterInternal(volName, podUID, &PhotonDiskUtil{}, plugin.host.GetMounter(plugin.GetPluginName())) -} - -func (plugin *photonPersistentDiskPlugin) newMounterInternal(spec *volume.Spec, podUID types.UID, manager pdManager, mounter mount.Interface) (volume.Mounter, error) { - vvol, _, err := getVolumeSource(spec) - if err != nil { - klog.Errorf("Photon volume plugin: newMounterInternal failed to get volume source") - return nil, err - } - - pdID := vvol.PdID - fsType := vvol.FSType - - return &photonPersistentDiskMounter{ - photonPersistentDisk: &photonPersistentDisk{ - podUID: podUID, - volName: spec.Name(), - pdID: pdID, - manager: manager, - mounter: mounter, - plugin: plugin, - }, - fsType: fsType, - diskMounter: util.NewSafeFormatAndMountFromHost(plugin.GetPluginName(), plugin.host), - mountOption: util.MountOptionFromSpec(spec), - }, nil -} - -func (plugin *photonPersistentDiskPlugin) newUnmounterInternal(volName string, podUID types.UID, manager pdManager, mounter mount.Interface) (volume.Unmounter, error) { - return &photonPersistentDiskUnmounter{ - &photonPersistentDisk{ - podUID: podUID, - volName: volName, - manager: manager, - mounter: mounter, - plugin: plugin, - }}, nil -} - -func (plugin *photonPersistentDiskPlugin) ConstructVolumeSpec(volumeSpecName, mountPath string) (*volume.Spec, error) { - mounter := plugin.host.GetMounter(plugin.GetPluginName()) - pluginMntDir := util.GetPluginMountDir(plugin.host, plugin.GetPluginName()) - pdID, err := mounter.GetDeviceNameFromMount(mountPath, pluginMntDir) - if err != nil { - return nil, err - } - - photonPersistentDisk := &v1.Volume{ - Name: volumeSpecName, - VolumeSource: v1.VolumeSource{ - PhotonPersistentDisk: &v1.PhotonPersistentDiskVolumeSource{ - PdID: pdID, - }, - }, - } - return volume.NewSpecFromVolume(photonPersistentDisk), nil -} - -// Abstract interface to disk operations. -type pdManager interface { - // Creates a volume - CreateVolume(provisioner *photonPersistentDiskProvisioner) (pdID string, volumeSizeGB int, fstype string, err error) - // Deletes a volume - DeleteVolume(deleter *photonPersistentDiskDeleter) error -} - -// photonPersistentDisk volumes are disk resources are attached to the kubelet's host machine and exposed to the pod. -type photonPersistentDisk struct { - volName string - podUID types.UID - // Unique identifier of the volume, used to find the disk resource in the provider. - pdID string - // Filesystem type, optional. - fsType string - // Utility interface that provides API calls to the provider to attach/detach disks. - manager pdManager - // Mounter interface that provides system calls to mount the global path to the pod local path. - mounter mount.Interface - plugin *photonPersistentDiskPlugin - volume.MetricsNil -} - -var _ volume.Mounter = &photonPersistentDiskMounter{} - -type photonPersistentDiskMounter struct { - *photonPersistentDisk - fsType string - diskMounter *mount.SafeFormatAndMount - mountOption []string -} - -func (b *photonPersistentDiskMounter) GetAttributes() volume.Attributes { - return volume.Attributes{ - SupportsSELinux: true, - } -} - -// Checks prior to mount operations to verify that the required components (binaries, etc.) -// to mount the volume are available on the underlying node. -// If not, it returns an error -func (b *photonPersistentDiskMounter) CanMount() error { - return nil -} - -// SetUp attaches the disk and bind mounts to the volume path. -func (b *photonPersistentDiskMounter) SetUp(mounterArgs volume.MounterArgs) error { - return b.SetUpAt(b.GetPath(), mounterArgs) -} - -// SetUp attaches the disk and bind mounts to the volume path. -func (b *photonPersistentDiskMounter) SetUpAt(dir string, mounterArgs volume.MounterArgs) error { - klog.V(4).Infof("Photon Persistent Disk setup %s to %s", b.pdID, dir) - - // TODO: handle failed mounts here. - notmnt, err := b.mounter.IsLikelyNotMountPoint(dir) - if err != nil && !os.IsNotExist(err) { - klog.Errorf("cannot validate mount point: %s %v", dir, err) - return err - } - if !notmnt { - return nil - } - - if err := os.MkdirAll(dir, 0750); err != nil { - klog.Errorf("mkdir failed on disk %s (%v)", dir, err) - return err - } - - options := []string{"bind"} - - // Perform a bind mount to the full path to allow duplicate mounts of the same PD. - globalPDPath := makeGlobalPDPath(b.plugin.host, b.pdID) - klog.V(4).Infof("attempting to mount %s", dir) - - mountOptions := util.JoinMountOptions(options, b.mountOption) - err = b.mounter.Mount(globalPDPath, dir, "", mountOptions) - if err != nil { - notmnt, mntErr := b.mounter.IsLikelyNotMountPoint(dir) - if mntErr != nil { - klog.Errorf("IsLikelyNotMountPoint check failed: %v", mntErr) - return err - } - if !notmnt { - if mntErr = b.mounter.Unmount(dir); mntErr != nil { - klog.Errorf("Failed to unmount: %v", mntErr) - return err - } - notmnt, mntErr := b.mounter.IsLikelyNotMountPoint(dir) - if mntErr != nil { - klog.Errorf("IsLikelyNotMountPoint check failed: %v", mntErr) - return err - } - if !notmnt { - klog.Errorf("%s is still mounted, despite call to unmount(). Will try again next sync loop.", b.GetPath()) - return err - } - } - os.Remove(dir) - klog.Errorf("Mount of disk %s failed: %v", dir, err) - return err - } - - return nil -} - -var _ volume.Unmounter = &photonPersistentDiskUnmounter{} - -type photonPersistentDiskUnmounter struct { - *photonPersistentDisk -} - -// Unmounts the bind mount, and detaches the disk only if the PD -// resource was the last reference to that disk on the kubelet. -func (c *photonPersistentDiskUnmounter) TearDown() error { - err := c.TearDownAt(c.GetPath()) - if err != nil { - return err - } - - removeFromScsiSubsystem(c.volName) - return nil -} - -// Unmounts the bind mount, and detaches the disk only if the PD -// resource was the last reference to that disk on the kubelet. -func (c *photonPersistentDiskUnmounter) TearDownAt(dir string) error { - return mount.CleanupMountPoint(dir, c.mounter, false) -} - -func makeGlobalPDPath(host volume.VolumeHost, devName string) string { - return filepath.Join(host.GetPluginDir(photonPersistentDiskPluginName), util.MountsInGlobalPDPath, devName) -} - -func (ppd *photonPersistentDisk) GetPath() string { - name := photonPersistentDiskPluginName - return ppd.plugin.host.GetPodVolumeDir(ppd.podUID, utilstrings.EscapeQualifiedName(name), ppd.volName) -} - -// TODO: supporting more access mode for PhotonController persistent disk -func (plugin *photonPersistentDiskPlugin) GetAccessModes() []v1.PersistentVolumeAccessMode { - return []v1.PersistentVolumeAccessMode{ - v1.ReadWriteOnce, - } -} - -type photonPersistentDiskDeleter struct { - *photonPersistentDisk -} - -var _ volume.Deleter = &photonPersistentDiskDeleter{} - -func (plugin *photonPersistentDiskPlugin) NewDeleter(spec *volume.Spec) (volume.Deleter, error) { - return plugin.newDeleterInternal(spec, &PhotonDiskUtil{}) -} - -func (plugin *photonPersistentDiskPlugin) newDeleterInternal(spec *volume.Spec, manager pdManager) (volume.Deleter, error) { - if spec.PersistentVolume != nil && spec.PersistentVolume.Spec.PhotonPersistentDisk == nil { - return nil, fmt.Errorf("spec.PersistentVolumeSource.PhotonPersistentDisk is nil") - } - return &photonPersistentDiskDeleter{ - &photonPersistentDisk{ - volName: spec.Name(), - pdID: spec.PersistentVolume.Spec.PhotonPersistentDisk.PdID, - manager: manager, - plugin: plugin, - }}, nil -} - -func (r *photonPersistentDiskDeleter) Delete() error { - return r.manager.DeleteVolume(r) -} - -type photonPersistentDiskProvisioner struct { - *photonPersistentDisk - options volume.VolumeOptions -} - -var _ volume.Provisioner = &photonPersistentDiskProvisioner{} - -func (plugin *photonPersistentDiskPlugin) NewProvisioner(options volume.VolumeOptions) (volume.Provisioner, error) { - return plugin.newProvisionerInternal(options, &PhotonDiskUtil{}) -} - -func (plugin *photonPersistentDiskPlugin) newProvisionerInternal(options volume.VolumeOptions, manager pdManager) (volume.Provisioner, error) { - return &photonPersistentDiskProvisioner{ - photonPersistentDisk: &photonPersistentDisk{ - manager: manager, - plugin: plugin, - }, - options: options, - }, nil -} - -func (p *photonPersistentDiskProvisioner) Provision(selectedNode *v1.Node, allowedTopologies []v1.TopologySelectorTerm) (*v1.PersistentVolume, error) { - if !util.AccessModesContainedInAll(p.plugin.GetAccessModes(), p.options.PVC.Spec.AccessModes) { - return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", p.options.PVC.Spec.AccessModes, p.plugin.GetAccessModes()) - } - - if util.CheckPersistentVolumeClaimModeBlock(p.options.PVC) { - return nil, fmt.Errorf("%s does not support block volume provisioning", p.plugin.GetPluginName()) - } - - pdID, sizeGB, fstype, err := p.manager.CreateVolume(p) - if err != nil { - return nil, err - } - - if fstype == "" { - fstype = "ext4" - } - - pv := &v1.PersistentVolume{ - ObjectMeta: metav1.ObjectMeta{ - Name: p.options.PVName, - Labels: map[string]string{}, - Annotations: map[string]string{ - util.VolumeDynamicallyCreatedByKey: "photon-volume-dynamic-provisioner", - }, - }, - Spec: v1.PersistentVolumeSpec{ - PersistentVolumeReclaimPolicy: p.options.PersistentVolumeReclaimPolicy, - AccessModes: p.options.PVC.Spec.AccessModes, - Capacity: v1.ResourceList{ - v1.ResourceName(v1.ResourceStorage): resource.MustParse(fmt.Sprintf("%dGi", sizeGB)), - }, - PersistentVolumeSource: v1.PersistentVolumeSource{ - PhotonPersistentDisk: &v1.PhotonPersistentDiskVolumeSource{ - PdID: pdID, - FSType: fstype, - }, - }, - MountOptions: p.options.MountOptions, - }, - } - if len(p.options.PVC.Spec.AccessModes) == 0 { - pv.Spec.AccessModes = p.plugin.GetAccessModes() - } - - return pv, nil -} - -func getVolumeSource( - spec *volume.Spec) (*v1.PhotonPersistentDiskVolumeSource, bool, error) { - if spec.Volume != nil && spec.Volume.PhotonPersistentDisk != nil { - return spec.Volume.PhotonPersistentDisk, spec.ReadOnly, nil - } else if spec.PersistentVolume != nil && - spec.PersistentVolume.Spec.PhotonPersistentDisk != nil { - return spec.PersistentVolume.Spec.PhotonPersistentDisk, spec.ReadOnly, nil - } - - return nil, false, fmt.Errorf("Spec does not reference a Photon Controller persistent disk type") -} diff --git a/pkg/volume/photon_pd/photon_pd_test.go b/pkg/volume/photon_pd/photon_pd_test.go deleted file mode 100644 index 8b2beaeb619..00000000000 --- a/pkg/volume/photon_pd/photon_pd_test.go +++ /dev/null @@ -1,235 +0,0 @@ -/* -Copyright 2014 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 photon_pd - -import ( - "fmt" - "os" - "path/filepath" - "testing" - - "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" - utiltesting "k8s.io/client-go/util/testing" - "k8s.io/kubernetes/pkg/util/mount" - "k8s.io/kubernetes/pkg/volume" - volumetest "k8s.io/kubernetes/pkg/volume/testing" -) - -func TestCanSupport(t *testing.T) { - tmpDir, err := utiltesting.MkTmpdir("photonpdTest") - if err != nil { - t.Fatalf("can't make a temp dir: %v", err) - } - defer os.RemoveAll(tmpDir) - plugMgr := volume.VolumePluginMgr{} - plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) - - plug, err := plugMgr.FindPluginByName("kubernetes.io/photon-pd") - if err != nil { - t.Errorf("Can't find the plugin by name") - } - if plug.GetPluginName() != "kubernetes.io/photon-pd" { - t.Errorf("Wrong name: %s", plug.GetPluginName()) - } - if !plug.CanSupport(&volume.Spec{Volume: &v1.Volume{VolumeSource: v1.VolumeSource{PhotonPersistentDisk: &v1.PhotonPersistentDiskVolumeSource{}}}}) { - t.Errorf("Expected true") - } - if !plug.CanSupport(&volume.Spec{PersistentVolume: &v1.PersistentVolume{Spec: v1.PersistentVolumeSpec{PersistentVolumeSource: v1.PersistentVolumeSource{PhotonPersistentDisk: &v1.PhotonPersistentDiskVolumeSource{}}}}}) { - t.Errorf("Expected true") - } -} - -func TestGetAccessModes(t *testing.T) { - tmpDir, err := utiltesting.MkTmpdir("photonpdTest") - if err != nil { - t.Fatalf("can't make a temp dir: %v", err) - } - defer os.RemoveAll(tmpDir) - plugMgr := volume.VolumePluginMgr{} - plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) - - plug, err := plugMgr.FindPersistentPluginByName("kubernetes.io/photon-pd") - if err != nil { - t.Errorf("Can't find the plugin by name") - } - - if !volumetest.ContainsAccessMode(plug.GetAccessModes(), v1.ReadWriteOnce) { - t.Errorf("Expected to support AccessModeTypes: %s", v1.ReadWriteOnce) - } - if volumetest.ContainsAccessMode(plug.GetAccessModes(), v1.ReadOnlyMany) { - t.Errorf("Expected not to support AccessModeTypes: %s", v1.ReadOnlyMany) - } -} - -type fakePDManager struct { -} - -func (fake *fakePDManager) CreateVolume(c *photonPersistentDiskProvisioner) (pdID string, volumeSizeGB int, fstype string, err error) { - return "test-photon-pd-id", 10, "ext4", nil -} - -func (fake *fakePDManager) DeleteVolume(cd *photonPersistentDiskDeleter) error { - if cd.pdID != "test-photon-pd-id" { - return fmt.Errorf("Deleter got unexpected volume name: %s", cd.pdID) - } - return nil -} - -func TestPlugin(t *testing.T) { - tmpDir, err := utiltesting.MkTmpdir("photonpdTest") - if err != nil { - t.Fatalf("can't make a temp dir: %v", err) - } - defer os.RemoveAll(tmpDir) - plugMgr := volume.VolumePluginMgr{} - plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) - - plug, err := plugMgr.FindPluginByName("kubernetes.io/photon-pd") - if err != nil { - t.Errorf("Can't find the plugin by name") - } - spec := &v1.Volume{ - Name: "vol1", - VolumeSource: v1.VolumeSource{ - PhotonPersistentDisk: &v1.PhotonPersistentDiskVolumeSource{ - PdID: "pdid", - FSType: "ext4", - }, - }, - } - fakeManager := &fakePDManager{} - fakeMounter := &mount.FakeMounter{} - mounter, err := plug.(*photonPersistentDiskPlugin).newMounterInternal(volume.NewSpecFromVolume(spec), types.UID("poduid"), fakeManager, fakeMounter) - if err != nil { - t.Errorf("Failed to make a new Mounter: %v", err) - } - if mounter == nil { - t.Errorf("Got a nil Mounter") - } - - volPath := filepath.Join(tmpDir, "pods/poduid/volumes/kubernetes.io~photon-pd/vol1") - path := mounter.GetPath() - if path != volPath { - t.Errorf("Got unexpected path: %s", path) - } - - if err := mounter.SetUp(volume.MounterArgs{}); err != nil { - t.Errorf("Expected success, got: %v", err) - } - if _, err := os.Stat(path); err != nil { - if os.IsNotExist(err) { - t.Errorf("SetUp() failed, volume path not created: %s", path) - } else { - t.Errorf("SetUp() failed: %v", err) - } - } - - fakeManager = &fakePDManager{} - unmounter, err := plug.(*photonPersistentDiskPlugin).newUnmounterInternal("vol1", types.UID("poduid"), fakeManager, fakeMounter) - if err != nil { - t.Errorf("Failed to make a new Unmounter: %v", err) - } - if unmounter == nil { - t.Errorf("Got a nil Unmounter") - } - - if err := unmounter.TearDown(); err != nil { - t.Errorf("Expected success, got: %v", err) - } - if _, err := os.Stat(path); err == nil { - t.Errorf("TearDown() failed, volume path still exists: %s", path) - } else if !os.IsNotExist(err) { - t.Errorf("TearDown() failed: %v", err) - } - - // Test Provisioner - options := volume.VolumeOptions{ - PVC: volumetest.CreateTestPVC("10Gi", []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}), - PersistentVolumeReclaimPolicy: v1.PersistentVolumeReclaimDelete, - } - provisioner, err := plug.(*photonPersistentDiskPlugin).newProvisionerInternal(options, &fakePDManager{}) - if err != nil { - t.Fatalf("Error creating new provisioner:%v", err) - } - persistentSpec, err := provisioner.Provision(nil, nil) - if err != nil { - t.Errorf("Provision() failed: %v", err) - } - - if persistentSpec.Spec.PersistentVolumeSource.PhotonPersistentDisk.PdID != "test-photon-pd-id" { - t.Errorf("Provision() returned unexpected persistent disk ID: %s", persistentSpec.Spec.PersistentVolumeSource.PhotonPersistentDisk.PdID) - } - cap := persistentSpec.Spec.Capacity[v1.ResourceStorage] - size := cap.Value() - if size != 10*1024*1024*1024 { - t.Errorf("Provision() returned unexpected volume size: %v", size) - } - - // Test Deleter - volSpec := &volume.Spec{ - PersistentVolume: persistentSpec, - } - deleter, err := plug.(*photonPersistentDiskPlugin).newDeleterInternal(volSpec, &fakePDManager{}) - if err != nil { - t.Fatalf("Error creating new deleter:%v", err) - } - err = deleter.Delete() - if err != nil { - t.Errorf("Deleter() failed: %v", err) - } -} - -func TestMounterAndUnmounterTypeAssert(t *testing.T) { - tmpDir, err := utiltesting.MkTmpdir("photonpdTest") - if err != nil { - t.Fatalf("can't make a temp dir: %v", err) - } - defer os.RemoveAll(tmpDir) - plugMgr := volume.VolumePluginMgr{} - plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) - - plug, err := plugMgr.FindPluginByName("kubernetes.io/photon-pd") - if err != nil { - t.Errorf("Can't find the plugin by name") - } - spec := &v1.Volume{ - Name: "vol1", - VolumeSource: v1.VolumeSource{ - PhotonPersistentDisk: &v1.PhotonPersistentDiskVolumeSource{ - PdID: "pdid", - FSType: "ext4", - }, - }, - } - - mounter, err := plug.(*photonPersistentDiskPlugin).newMounterInternal(volume.NewSpecFromVolume(spec), types.UID("poduid"), &fakePDManager{}, &mount.FakeMounter{}) - if err != nil { - t.Fatalf("Error creating new mounter:%v", err) - } - if _, ok := mounter.(volume.Unmounter); ok { - t.Errorf("Volume Mounter can be type-assert to Unmounter") - } - - unmounter, err := plug.(*photonPersistentDiskPlugin).newUnmounterInternal("vol1", types.UID("poduid"), &fakePDManager{}, &mount.FakeMounter{}) - if err != nil { - t.Fatalf("Error creating new unmounter:%v", err) - } - if _, ok := unmounter.(volume.Mounter); ok { - t.Errorf("Volume Unmounter can be type-assert to Mounter") - } -} diff --git a/pkg/volume/photon_pd/photon_util.go b/pkg/volume/photon_pd/photon_util.go deleted file mode 100644 index b9865d24472..00000000000 --- a/pkg/volume/photon_pd/photon_util.go +++ /dev/null @@ -1,157 +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 photon_pd - -import ( - "errors" - "fmt" - "io/ioutil" - "strings" - "time" - - "k8s.io/api/core/v1" - cloudprovider "k8s.io/cloud-provider" - volumehelpers "k8s.io/cloud-provider/volume/helpers" - "k8s.io/klog" - "k8s.io/kubernetes/pkg/cloudprovider/providers/photon" - "k8s.io/kubernetes/pkg/util/mount" - "k8s.io/kubernetes/pkg/volume" - volumeutil "k8s.io/kubernetes/pkg/volume/util" -) - -const ( - maxRetries = 10 - checkSleepDuration = time.Second - diskByIDPath = "/dev/disk/by-id/" - diskPhotonPrefix = "wwn-0x" -) - -var ErrProbeVolume = errors.New("Error scanning attached volumes") - -// volNameToDeviceName is a mapping between spec.Name from detacher -// and the device name inside scsi path. Once pvscsi controller is -// supported, this won't be needed. -var volNameToDeviceName = make(map[string]string) - -type PhotonDiskUtil struct{} - -func removeFromScsiSubsystem(volName string) { - // TODO: if using pvscsi controller, this won't be needed - deviceName := volNameToDeviceName[volName] - fileName := "/sys/block/" + deviceName + "/device/delete" - data := []byte("1") - ioutil.WriteFile(fileName, data, 0666) -} - -func scsiHostScan() { - // TODO: if using pvscsi controller, this won't be needed - scsi_path := "/sys/class/scsi_host/" - if dirs, err := ioutil.ReadDir(scsi_path); err == nil { - for _, f := range dirs { - name := scsi_path + f.Name() + "/scan" - data := []byte("- - -") - ioutil.WriteFile(name, data, 0666) - klog.Errorf("scsiHostScan scan for %s", name) - } - } -} - -func verifyDevicePath(path string) (string, error) { - if pathExists, err := mount.PathExists(path); err != nil { - return "", fmt.Errorf("Error checking if path exists: %v", err) - } else if pathExists { - return path, nil - } - - klog.V(4).Infof("verifyDevicePath: path not exists yet") - return "", nil -} - -// CreateVolume creates a PhotonController persistent disk. -func (util *PhotonDiskUtil) CreateVolume(p *photonPersistentDiskProvisioner) (pdID string, capacityGB int, fstype string, err error) { - cloud, err := getCloudProvider(p.plugin.host.GetCloudProvider()) - if err != nil { - klog.Errorf("Photon Controller Util: CreateVolume failed to get cloud provider. Error [%v]", err) - return "", 0, "", err - } - - capacity := p.options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)] - // PhotonController works with GiB, convert to GiB with rounding up - volSizeGB, err := volumehelpers.RoundUpToGiBInt(capacity) - if err != nil { - return "", 0, "", err - } - name := volumeutil.GenerateVolumeName(p.options.ClusterName, p.options.PVName, 255) - volumeOptions := &photon.VolumeOptions{ - CapacityGB: volSizeGB, - Tags: *p.options.CloudTags, - Name: name, - } - - for parameter, value := range p.options.Parameters { - switch strings.ToLower(parameter) { - case "flavor": - volumeOptions.Flavor = value - case volume.VolumeParameterFSType: - fstype = value - klog.V(4).Infof("Photon Controller Util: Setting fstype to %s", fstype) - default: - klog.Errorf("Photon Controller Util: invalid option %s for volume plugin %s.", parameter, p.plugin.GetPluginName()) - return "", 0, "", fmt.Errorf("Photon Controller Util: invalid option %s for volume plugin %s.", parameter, p.plugin.GetPluginName()) - } - } - - pdID, err = cloud.CreateDisk(volumeOptions) - if err != nil { - klog.Errorf("Photon Controller Util: failed to CreateDisk. Error [%v]", err) - return "", 0, "", err - } - - klog.V(4).Infof("Successfully created Photon Controller persistent disk %s", name) - return pdID, volSizeGB, "", nil -} - -// DeleteVolume deletes a vSphere volume. -func (util *PhotonDiskUtil) DeleteVolume(pd *photonPersistentDiskDeleter) error { - cloud, err := getCloudProvider(pd.plugin.host.GetCloudProvider()) - if err != nil { - klog.Errorf("Photon Controller Util: DeleteVolume failed to get cloud provider. Error [%v]", err) - return err - } - - if err = cloud.DeleteDisk(pd.pdID); err != nil { - klog.Errorf("Photon Controller Util: failed to DeleteDisk for pdID %s. Error [%v]", pd.pdID, err) - return err - } - - klog.V(4).Infof("Successfully deleted PhotonController persistent disk %s", pd.pdID) - return nil -} - -func getCloudProvider(cloud cloudprovider.Interface) (*photon.PCCloud, error) { - if cloud == nil { - klog.Errorf("Photon Controller Util: Cloud provider not initialized properly") - return nil, fmt.Errorf("Photon Controller Util: Cloud provider not initialized properly") - } - - pcc := cloud.(*photon.PCCloud) - if pcc == nil { - klog.Errorf("Invalid cloud provider: expected Photon Controller") - return nil, fmt.Errorf("Invalid cloud provider: expected Photon Controller") - } - return pcc, nil -} diff --git a/staging/src/k8s.io/cloud-provider/plugins.go b/staging/src/k8s.io/cloud-provider/plugins.go index 9fc6aff8cd2..e8a41d87aac 100644 --- a/staging/src/k8s.io/cloud-provider/plugins.go +++ b/staging/src/k8s.io/cloud-provider/plugins.go @@ -42,11 +42,8 @@ var ( }{ {"aws", false, "The AWS provider is deprecated and will be removed in a future release"}, {"azure", false, "The Azure provider is deprecated and will be removed in a future release"}, - {"cloudstack", false, "The CloudStack Controller project is no longer maintained."}, {"gce", false, "The GCE provider is deprecated and will be removed in a future release"}, {"openstack", true, "https://github.com/kubernetes/cloud-provider-openstack"}, - {"ovirt", false, "The ovirt Controller project is no longer maintained."}, - {"photon", false, "The Photon Controller project is no longer maintained."}, {"vsphere", false, "The vSphere provider is deprecated and will be removed in a future release"}, } ) diff --git a/test/test_owners.csv b/test/test_owners.csv index 8f3d51da648..05d1948f23b 100644 --- a/test/test_owners.csv +++ b/test/test_owners.csv @@ -583,12 +583,8 @@ k8s.io/kubernetes/pkg/client/tests,Q-Lee,1, k8s.io/kubernetes/pkg/client/unversioned,justinsb,1, k8s.io/kubernetes/pkg/cloudprovider/providers/aws,eparis,1, k8s.io/kubernetes/pkg/cloudprovider/providers/azure,saad-ali,1, -k8s.io/kubernetes/pkg/cloudprovider/providers/cloudstack,roberthbailey,1, k8s.io/kubernetes/pkg/cloudprovider/providers/gce,yifan-gu,1, -k8s.io/kubernetes/pkg/cloudprovider/providers/mesos,mml,1, k8s.io/kubernetes/pkg/cloudprovider/providers/openstack,Q-Lee,1, -k8s.io/kubernetes/pkg/cloudprovider/providers/ovirt,dchen1107,1, -k8s.io/kubernetes/pkg/cloudprovider/providers/photon,luomiao,0, k8s.io/kubernetes/pkg/cloudprovider/providers/vsphere,apelisse,1, k8s.io/kubernetes/pkg/controller,mikedanese,1, k8s.io/kubernetes/pkg/controller/bootstrap,mikedanese,0, @@ -821,7 +817,6 @@ k8s.io/kubernetes/pkg/volume/glusterfs,tallclair,1, k8s.io/kubernetes/pkg/volume/hostpath,jbeda,1, k8s.io/kubernetes/pkg/volume/iscsi,cjcullen,1, k8s.io/kubernetes/pkg/volume/nfs,justinsb,1, -k8s.io/kubernetes/pkg/volume/photon_pd,luomiao,0, k8s.io/kubernetes/pkg/volume/projected,kevin-wangzefeng,1, k8s.io/kubernetes/pkg/volume/quobyte,yujuhong,1, k8s.io/kubernetes/pkg/volume/rbd,piosz,1,