mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-14 10:53:34 +00:00
vendor files
This commit is contained in:
130
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/BUILD
generated
vendored
Normal file
130
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/BUILD
generated
vendored
Normal file
@ -0,0 +1,130 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"gce.go",
|
||||
"gce_address_manager.go",
|
||||
"gce_addresses.go",
|
||||
"gce_addresses_fakes.go",
|
||||
"gce_alpha.go",
|
||||
"gce_annotations.go",
|
||||
"gce_backendservice.go",
|
||||
"gce_cert.go",
|
||||
"gce_clusterid.go",
|
||||
"gce_clusters.go",
|
||||
"gce_disks.go",
|
||||
"gce_firewall.go",
|
||||
"gce_forwardingrule.go",
|
||||
"gce_forwardingrule_fakes.go",
|
||||
"gce_healthchecks.go",
|
||||
"gce_instancegroup.go",
|
||||
"gce_instances.go",
|
||||
"gce_interfaces.go",
|
||||
"gce_loadbalancer.go",
|
||||
"gce_loadbalancer_external.go",
|
||||
"gce_loadbalancer_internal.go",
|
||||
"gce_loadbalancer_naming.go",
|
||||
"gce_networkendpointgroup.go",
|
||||
"gce_op.go",
|
||||
"gce_routes.go",
|
||||
"gce_targetpool.go",
|
||||
"gce_targetproxy.go",
|
||||
"gce_urlmap.go",
|
||||
"gce_util.go",
|
||||
"gce_zones.go",
|
||||
"metrics.go",
|
||||
"token_source.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/cloudprovider/providers/gce",
|
||||
deps = [
|
||||
"//pkg/api/v1/service:go_default_library",
|
||||
"//pkg/cloudprovider:go_default_library",
|
||||
"//pkg/controller:go_default_library",
|
||||
"//pkg/kubelet/apis:go_default_library",
|
||||
"//pkg/master/ports:go_default_library",
|
||||
"//pkg/util/net/sets:go_default_library",
|
||||
"//pkg/util/version:go_default_library",
|
||||
"//pkg/version:go_default_library",
|
||||
"//pkg/volume:go_default_library",
|
||||
"//pkg/volume/util:go_default_library",
|
||||
"//vendor/cloud.google.com/go/compute/metadata:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/github.com/prometheus/client_golang/prometheus:go_default_library",
|
||||
"//vendor/golang.org/x/oauth2:go_default_library",
|
||||
"//vendor/golang.org/x/oauth2/google:go_default_library",
|
||||
"//vendor/google.golang.org/api/compute/v0.alpha:go_default_library",
|
||||
"//vendor/google.golang.org/api/compute/v0.beta:go_default_library",
|
||||
"//vendor/google.golang.org/api/compute/v1:go_default_library",
|
||||
"//vendor/google.golang.org/api/container/v1:go_default_library",
|
||||
"//vendor/google.golang.org/api/googleapi:go_default_library",
|
||||
"//vendor/gopkg.in/gcfg.v1:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/fields:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
|
||||
"//vendor/k8s.io/client-go/informers:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/scheme:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/cache:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/record:go_default_library",
|
||||
"//vendor/k8s.io/client-go/util/flowcontrol:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"gce_address_manager_test.go",
|
||||
"gce_annotations_test.go",
|
||||
"gce_disks_test.go",
|
||||
"gce_healthchecks_test.go",
|
||||
"gce_loadbalancer_external_test.go",
|
||||
"gce_test.go",
|
||||
"gce_util_test.go",
|
||||
"metrics_test.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/cloudprovider/providers/gce",
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//pkg/cloudprovider:go_default_library",
|
||||
"//pkg/kubelet/apis:go_default_library",
|
||||
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
||||
"//vendor/github.com/stretchr/testify/require:go_default_library",
|
||||
"//vendor/golang.org/x/oauth2/google:go_default_library",
|
||||
"//vendor/google.golang.org/api/compute/v0.alpha:go_default_library",
|
||||
"//vendor/google.golang.org/api/compute/v0.beta:go_default_library",
|
||||
"//vendor/google.golang.org/api/compute/v1:go_default_library",
|
||||
"//vendor/google.golang.org/api/googleapi:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
8
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/OWNERS
generated
vendored
Normal file
8
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/OWNERS
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
approvers:
|
||||
- saad-ali
|
||||
- jingxu97
|
||||
- bowei
|
||||
- freehan
|
||||
- nicksardo
|
||||
- mrhohn
|
||||
- dnardo
|
19
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/doc.go
generated
vendored
Normal file
19
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/doc.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
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 gce is an implementation of Interface, LoadBalancer
|
||||
// and Instances for Google Compute Engine.
|
||||
package gce // import "k8s.io/kubernetes/pkg/cloudprovider/providers/gce"
|
880
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce.go
generated
vendored
Normal file
880
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce.go
generated
vendored
Normal file
@ -0,0 +1,880 @@
|
||||
/*
|
||||
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 gce
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
gcfg "gopkg.in/gcfg.v1"
|
||||
|
||||
"cloud.google.com/go/compute/metadata"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/informers"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/tools/record"
|
||||
"k8s.io/client-go/util/flowcontrol"
|
||||
"k8s.io/kubernetes/pkg/cloudprovider"
|
||||
"k8s.io/kubernetes/pkg/controller"
|
||||
kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis"
|
||||
"k8s.io/kubernetes/pkg/version"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/google"
|
||||
computealpha "google.golang.org/api/compute/v0.alpha"
|
||||
computebeta "google.golang.org/api/compute/v0.beta"
|
||||
compute "google.golang.org/api/compute/v1"
|
||||
container "google.golang.org/api/container/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
ProviderName = "gce"
|
||||
|
||||
k8sNodeRouteTag = "k8s-node-route"
|
||||
|
||||
// AffinityTypeNone - no session affinity.
|
||||
gceAffinityTypeNone = "NONE"
|
||||
// AffinityTypeClientIP - affinity based on Client IP.
|
||||
gceAffinityTypeClientIP = "CLIENT_IP"
|
||||
// AffinityTypeClientIPProto - affinity based on Client IP and port.
|
||||
gceAffinityTypeClientIPProto = "CLIENT_IP_PROTO"
|
||||
|
||||
operationPollInterval = 3 * time.Second
|
||||
// Creating Route in very large clusters, may take more than half an hour.
|
||||
operationPollTimeoutDuration = time.Hour
|
||||
|
||||
// Each page can have 500 results, but we cap how many pages
|
||||
// are iterated through to prevent infinite loops if the API
|
||||
// were to continuously return a nextPageToken.
|
||||
maxPages = 25
|
||||
|
||||
maxTargetPoolCreateInstances = 200
|
||||
|
||||
// HTTP Load Balancer parameters
|
||||
// Configure 2 second period for external health checks.
|
||||
gceHcCheckIntervalSeconds = int64(2)
|
||||
gceHcTimeoutSeconds = int64(1)
|
||||
// Start sending requests as soon as a pod is found on the node.
|
||||
gceHcHealthyThreshold = int64(1)
|
||||
// Defaults to 5 * 2 = 10 seconds before the LB will steer traffic away
|
||||
gceHcUnhealthyThreshold = int64(5)
|
||||
|
||||
gceComputeAPIEndpoint = "https://www.googleapis.com/compute/v1/"
|
||||
gceComputeAPIEndpointAlpha = "https://www.googleapis.com/compute/alpha/"
|
||||
)
|
||||
|
||||
// gceObject is an abstraction of all GCE API object in go client
|
||||
type gceObject interface {
|
||||
MarshalJSON() ([]byte, error)
|
||||
}
|
||||
|
||||
// GCECloud is an implementation of Interface, LoadBalancer and Instances for Google Compute Engine.
|
||||
type GCECloud struct {
|
||||
// ClusterID contains functionality for getting (and initializing) the ingress-uid. Call GCECloud.Initialize()
|
||||
// for the cloudprovider to start watching the configmap.
|
||||
ClusterID ClusterID
|
||||
|
||||
service *compute.Service
|
||||
serviceBeta *computebeta.Service
|
||||
serviceAlpha *computealpha.Service
|
||||
containerService *container.Service
|
||||
client clientset.Interface
|
||||
clientBuilder controller.ControllerClientBuilder
|
||||
eventBroadcaster record.EventBroadcaster
|
||||
eventRecorder record.EventRecorder
|
||||
projectID string
|
||||
region string
|
||||
localZone string // The zone in which we are running
|
||||
// managedZones will be set to the 1 zone if running a single zone cluster
|
||||
// it will be set to ALL zones in region for any multi-zone cluster
|
||||
// Use GetAllCurrentZones to get only zones that contain nodes
|
||||
managedZones []string
|
||||
networkURL string
|
||||
isLegacyNetwork bool
|
||||
subnetworkURL string
|
||||
secondaryRangeName string
|
||||
networkProjectID string
|
||||
onXPN bool
|
||||
nodeTags []string // List of tags to use on firewall rules for load balancers
|
||||
lastComputedNodeTags []string // List of node tags calculated in GetHostTags()
|
||||
lastKnownNodeNames sets.String // List of hostnames used to calculate lastComputedHostTags in GetHostTags(names)
|
||||
computeNodeTagLock sync.Mutex // Lock for computing and setting node tags
|
||||
nodeInstancePrefix string // If non-"", an advisory prefix for all nodes in the cluster
|
||||
useMetadataServer bool
|
||||
operationPollRateLimiter flowcontrol.RateLimiter
|
||||
manager diskServiceManager
|
||||
// Lock for access to nodeZones
|
||||
nodeZonesLock sync.Mutex
|
||||
// nodeZones is a mapping from Zone to a sets.String of Node's names in the Zone
|
||||
// it is updated by the nodeInformer
|
||||
nodeZones map[string]sets.String
|
||||
nodeInformerSynced cache.InformerSynced
|
||||
// sharedResourceLock is used to serialize GCE operations that may mutate shared state to
|
||||
// prevent inconsistencies. For example, load balancers manipulation methods will take the
|
||||
// lock to prevent shared resources from being prematurely deleted while the operation is
|
||||
// in progress.
|
||||
sharedResourceLock sync.Mutex
|
||||
// AlphaFeatureGate gates gce alpha features in GCECloud instance.
|
||||
// Related wrapper functions that interacts with gce alpha api should examine whether
|
||||
// the corresponding api is enabled.
|
||||
// If not enabled, it should return error.
|
||||
AlphaFeatureGate *AlphaFeatureGate
|
||||
}
|
||||
|
||||
// TODO: replace gcfg with json
|
||||
type ConfigGlobal struct {
|
||||
TokenURL string `gcfg:"token-url"`
|
||||
TokenBody string `gcfg:"token-body"`
|
||||
// ProjectID and NetworkProjectID can either be the numeric or string-based
|
||||
// unique identifier that starts with [a-z].
|
||||
ProjectID string `gcfg:"project-id"`
|
||||
// NetworkProjectID refers to the project which owns the network being used.
|
||||
NetworkProjectID string `gcfg:"network-project-id"`
|
||||
NetworkName string `gcfg:"network-name"`
|
||||
SubnetworkName string `gcfg:"subnetwork-name"`
|
||||
// SecondaryRangeName is the name of the secondary range to allocate IP
|
||||
// aliases. The secondary range must be present on the subnetwork the
|
||||
// cluster is attached to.
|
||||
SecondaryRangeName string `gcfg:"secondary-range-name"`
|
||||
NodeTags []string `gcfg:"node-tags"`
|
||||
NodeInstancePrefix string `gcfg:"node-instance-prefix"`
|
||||
Multizone bool `gcfg:"multizone"`
|
||||
// ApiEndpoint is the GCE compute API endpoint to use. If this is blank,
|
||||
// then the default endpoint is used.
|
||||
ApiEndpoint string `gcfg:"api-endpoint"`
|
||||
// LocalZone specifies the GCE zone that gce cloud client instance is
|
||||
// located in (i.e. where the controller will be running). If this is
|
||||
// blank, then the local zone will be discovered via the metadata server.
|
||||
LocalZone string `gcfg:"local-zone"`
|
||||
// Default to none.
|
||||
// For example: MyFeatureFlag
|
||||
AlphaFeatures []string `gcfg:"alpha-features"`
|
||||
}
|
||||
|
||||
// ConfigFile is the struct used to parse the /etc/gce.conf configuration file.
|
||||
type ConfigFile struct {
|
||||
Global ConfigGlobal `gcfg:"global"`
|
||||
}
|
||||
|
||||
// CloudConfig includes all the necessary configuration for creating GCECloud
|
||||
type CloudConfig struct {
|
||||
ApiEndpoint string
|
||||
ProjectID string
|
||||
NetworkProjectID string
|
||||
Region string
|
||||
Zone string
|
||||
ManagedZones []string
|
||||
NetworkName string
|
||||
NetworkURL string
|
||||
SubnetworkName string
|
||||
SubnetworkURL string
|
||||
SecondaryRangeName string
|
||||
NodeTags []string
|
||||
NodeInstancePrefix string
|
||||
TokenSource oauth2.TokenSource
|
||||
UseMetadataServer bool
|
||||
AlphaFeatureGate *AlphaFeatureGate
|
||||
}
|
||||
|
||||
func init() {
|
||||
cloudprovider.RegisterCloudProvider(
|
||||
ProviderName,
|
||||
func(config io.Reader) (cloudprovider.Interface, error) {
|
||||
return newGCECloud(config)
|
||||
})
|
||||
}
|
||||
|
||||
// Raw access to the underlying GCE service, probably should only be used for e2e tests
|
||||
func (g *GCECloud) GetComputeService() *compute.Service {
|
||||
return g.service
|
||||
}
|
||||
|
||||
// newGCECloud creates a new instance of GCECloud.
|
||||
func newGCECloud(config io.Reader) (gceCloud *GCECloud, err error) {
|
||||
var cloudConfig *CloudConfig
|
||||
var configFile *ConfigFile
|
||||
|
||||
if config != nil {
|
||||
configFile, err = readConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
glog.Infof("Using GCE provider config %+v", configFile)
|
||||
}
|
||||
|
||||
cloudConfig, err = generateCloudConfig(configFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return CreateGCECloud(cloudConfig)
|
||||
|
||||
}
|
||||
|
||||
func readConfig(reader io.Reader) (*ConfigFile, error) {
|
||||
cfg := &ConfigFile{}
|
||||
if err := gcfg.FatalOnly(gcfg.ReadInto(cfg, reader)); err != nil {
|
||||
glog.Errorf("Couldn't read config: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func generateCloudConfig(configFile *ConfigFile) (cloudConfig *CloudConfig, err error) {
|
||||
cloudConfig = &CloudConfig{}
|
||||
// By default, fetch token from GCE metadata server
|
||||
cloudConfig.TokenSource = google.ComputeTokenSource("")
|
||||
cloudConfig.UseMetadataServer = true
|
||||
|
||||
featureMap := make(map[string]bool)
|
||||
cloudConfig.AlphaFeatureGate = &AlphaFeatureGate{featureMap}
|
||||
if configFile != nil {
|
||||
if configFile.Global.ApiEndpoint != "" {
|
||||
cloudConfig.ApiEndpoint = configFile.Global.ApiEndpoint
|
||||
}
|
||||
|
||||
if configFile.Global.TokenURL != "" {
|
||||
// if tokenURL is nil, set tokenSource to nil. This will force the OAuth client to fall
|
||||
// back to use DefaultTokenSource. This allows running gceCloud remotely.
|
||||
if configFile.Global.TokenURL == "nil" {
|
||||
cloudConfig.TokenSource = nil
|
||||
} else {
|
||||
cloudConfig.TokenSource = NewAltTokenSource(configFile.Global.TokenURL, configFile.Global.TokenBody)
|
||||
}
|
||||
}
|
||||
|
||||
cloudConfig.NodeTags = configFile.Global.NodeTags
|
||||
cloudConfig.NodeInstancePrefix = configFile.Global.NodeInstancePrefix
|
||||
|
||||
alphaFeatureGate, err := NewAlphaFeatureGate(configFile.Global.AlphaFeatures)
|
||||
if err != nil {
|
||||
glog.Errorf("Encountered error for creating alpha feature gate: %v", err)
|
||||
}
|
||||
cloudConfig.AlphaFeatureGate = alphaFeatureGate
|
||||
} else {
|
||||
// initialize AlphaFeatureGate when no AlphaFeatures are configured.
|
||||
alphaFeatureGate, err := NewAlphaFeatureGate([]string{})
|
||||
if err != nil {
|
||||
glog.Errorf("Encountered error for initializing alpha feature gate: %v", err)
|
||||
}
|
||||
cloudConfig.AlphaFeatureGate = alphaFeatureGate
|
||||
}
|
||||
|
||||
// retrieve projectID and zone
|
||||
if configFile == nil || configFile.Global.ProjectID == "" || configFile.Global.LocalZone == "" {
|
||||
cloudConfig.ProjectID, cloudConfig.Zone, err = getProjectAndZone()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if configFile != nil {
|
||||
if configFile.Global.ProjectID != "" {
|
||||
cloudConfig.ProjectID = configFile.Global.ProjectID
|
||||
}
|
||||
if configFile.Global.LocalZone != "" {
|
||||
cloudConfig.Zone = configFile.Global.LocalZone
|
||||
}
|
||||
if configFile.Global.NetworkProjectID != "" {
|
||||
cloudConfig.NetworkProjectID = configFile.Global.NetworkProjectID
|
||||
}
|
||||
}
|
||||
|
||||
// retrieve region
|
||||
cloudConfig.Region, err = GetGCERegion(cloudConfig.Zone)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// generate managedZones
|
||||
cloudConfig.ManagedZones = []string{cloudConfig.Zone}
|
||||
if configFile != nil && configFile.Global.Multizone {
|
||||
cloudConfig.ManagedZones = nil // Use all zones in region
|
||||
}
|
||||
|
||||
// Determine if network parameter is URL or Name
|
||||
if configFile != nil && configFile.Global.NetworkName != "" {
|
||||
if strings.Contains(configFile.Global.NetworkName, "/") {
|
||||
cloudConfig.NetworkURL = configFile.Global.NetworkName
|
||||
} else {
|
||||
cloudConfig.NetworkName = configFile.Global.NetworkName
|
||||
}
|
||||
} else {
|
||||
cloudConfig.NetworkName, err = getNetworkNameViaMetadata()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Determine if subnetwork parameter is URL or Name
|
||||
// If cluster is on a GCP network of mode=custom, then `SubnetName` must be specified in config file.
|
||||
if configFile != nil && configFile.Global.SubnetworkName != "" {
|
||||
if strings.Contains(configFile.Global.SubnetworkName, "/") {
|
||||
cloudConfig.SubnetworkURL = configFile.Global.SubnetworkName
|
||||
} else {
|
||||
cloudConfig.SubnetworkName = configFile.Global.SubnetworkName
|
||||
}
|
||||
}
|
||||
|
||||
if configFile != nil {
|
||||
cloudConfig.SecondaryRangeName = configFile.Global.SecondaryRangeName
|
||||
}
|
||||
|
||||
return cloudConfig, err
|
||||
}
|
||||
|
||||
// CreateGCECloud creates a GCECloud object using the specified parameters.
|
||||
// If no networkUrl is specified, loads networkName via rest call.
|
||||
// If no tokenSource is specified, uses oauth2.DefaultTokenSource.
|
||||
// If managedZones is nil / empty all zones in the region will be managed.
|
||||
func CreateGCECloud(config *CloudConfig) (*GCECloud, error) {
|
||||
// Remove any pre-release version and build metadata from the semver, leaving only the MAJOR.MINOR.PATCH portion.
|
||||
// See http://semver.org/.
|
||||
version := strings.TrimLeft(strings.Split(strings.Split(version.Get().GitVersion, "-")[0], "+")[0], "v")
|
||||
|
||||
// Create a user-agent header append string to supply to the Google API clients, to identify Kubernetes as the origin of the GCP API calls.
|
||||
userAgent := fmt.Sprintf("Kubernetes/%s (%s %s)", version, runtime.GOOS, runtime.GOARCH)
|
||||
|
||||
// Use ProjectID for NetworkProjectID, if it wasn't explicitly set.
|
||||
if config.NetworkProjectID == "" {
|
||||
config.NetworkProjectID = config.ProjectID
|
||||
}
|
||||
|
||||
client, err := newOauthClient(config.TokenSource)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
service, err := compute.New(client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
service.UserAgent = userAgent
|
||||
|
||||
client, err = newOauthClient(config.TokenSource)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
serviceBeta, err := computebeta.New(client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
serviceBeta.UserAgent = userAgent
|
||||
|
||||
client, err = newOauthClient(config.TokenSource)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
serviceAlpha, err := computealpha.New(client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
serviceAlpha.UserAgent = userAgent
|
||||
|
||||
// Expect override api endpoint to always be v1 api and follows the same pattern as prod.
|
||||
// Generate alpha and beta api endpoints based on override v1 api endpoint.
|
||||
// For example,
|
||||
// staging API endpoint: https://www.googleapis.com/compute/staging_v1/
|
||||
if config.ApiEndpoint != "" {
|
||||
service.BasePath = fmt.Sprintf("%sprojects/", config.ApiEndpoint)
|
||||
serviceBeta.BasePath = fmt.Sprintf("%sprojects/", strings.Replace(config.ApiEndpoint, "v1", "beta", -1))
|
||||
serviceAlpha.BasePath = fmt.Sprintf("%sprojects/", strings.Replace(config.ApiEndpoint, "v1", "alpha", -1))
|
||||
}
|
||||
|
||||
containerService, err := container.New(client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
containerService.UserAgent = userAgent
|
||||
|
||||
// ProjectID and.NetworkProjectID may be project number or name.
|
||||
projID, netProjID := tryConvertToProjectNames(config.ProjectID, config.NetworkProjectID, service)
|
||||
onXPN := projID != netProjID
|
||||
|
||||
var networkURL string
|
||||
var subnetURL string
|
||||
var isLegacyNetwork bool
|
||||
|
||||
if config.NetworkURL != "" {
|
||||
networkURL = config.NetworkURL
|
||||
} else if config.NetworkName != "" {
|
||||
networkURL = gceNetworkURL(config.ApiEndpoint, netProjID, config.NetworkName)
|
||||
} else {
|
||||
// Other consumers may use the cloudprovider without utilizing the wrapped GCE API functions
|
||||
// or functions requiring network/subnetwork URLs (e.g. Kubelet).
|
||||
glog.Warningf("No network name or URL specified.")
|
||||
}
|
||||
|
||||
if config.SubnetworkURL != "" {
|
||||
subnetURL = config.SubnetworkURL
|
||||
} else if config.SubnetworkName != "" {
|
||||
subnetURL = gceSubnetworkURL(config.ApiEndpoint, netProjID, config.Region, config.SubnetworkName)
|
||||
} else {
|
||||
// Determine the type of network and attempt to discover the correct subnet for AUTO mode.
|
||||
// Gracefully fail because kubelet calls CreateGCECloud without any config, and minions
|
||||
// lack the proper credentials for API calls.
|
||||
if networkName := lastComponent(networkURL); networkName != "" {
|
||||
var n *compute.Network
|
||||
if n, err = getNetwork(service, netProjID, networkName); err != nil {
|
||||
glog.Warningf("Could not retrieve network %q; err: %v", networkName, err)
|
||||
} else {
|
||||
switch typeOfNetwork(n) {
|
||||
case netTypeLegacy:
|
||||
glog.Infof("Network %q is type legacy - no subnetwork", networkName)
|
||||
isLegacyNetwork = true
|
||||
case netTypeCustom:
|
||||
glog.Warningf("Network %q is type custom - cannot auto select a subnetwork", networkName)
|
||||
case netTypeAuto:
|
||||
subnetURL, err = determineSubnetURL(service, netProjID, networkName, config.Region)
|
||||
if err != nil {
|
||||
glog.Warningf("Could not determine subnetwork for network %q and region %v; err: %v", networkName, config.Region, err)
|
||||
} else {
|
||||
glog.Infof("Auto selecting subnetwork %q", subnetURL)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(config.ManagedZones) == 0 {
|
||||
config.ManagedZones, err = getZonesForRegion(service, config.ProjectID, config.Region)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if len(config.ManagedZones) > 1 {
|
||||
glog.Infof("managing multiple zones: %v", config.ManagedZones)
|
||||
}
|
||||
|
||||
operationPollRateLimiter := flowcontrol.NewTokenBucketRateLimiter(10, 100) // 10 qps, 100 bucket size.
|
||||
|
||||
gce := &GCECloud{
|
||||
service: service,
|
||||
serviceAlpha: serviceAlpha,
|
||||
serviceBeta: serviceBeta,
|
||||
containerService: containerService,
|
||||
projectID: projID,
|
||||
networkProjectID: netProjID,
|
||||
onXPN: onXPN,
|
||||
region: config.Region,
|
||||
localZone: config.Zone,
|
||||
managedZones: config.ManagedZones,
|
||||
networkURL: networkURL,
|
||||
isLegacyNetwork: isLegacyNetwork,
|
||||
subnetworkURL: subnetURL,
|
||||
secondaryRangeName: config.SecondaryRangeName,
|
||||
nodeTags: config.NodeTags,
|
||||
nodeInstancePrefix: config.NodeInstancePrefix,
|
||||
useMetadataServer: config.UseMetadataServer,
|
||||
operationPollRateLimiter: operationPollRateLimiter,
|
||||
AlphaFeatureGate: config.AlphaFeatureGate,
|
||||
nodeZones: map[string]sets.String{},
|
||||
}
|
||||
|
||||
gce.manager = &gceServiceManager{gce}
|
||||
|
||||
return gce, nil
|
||||
}
|
||||
|
||||
// determineSubnetURL queries for all subnetworks in a region for a given network and returns
|
||||
// the URL of the subnetwork which exists in the auto-subnet range.
|
||||
func determineSubnetURL(service *compute.Service, networkProjectID, networkName, region string) (string, error) {
|
||||
subnets, err := listSubnetworksOfNetwork(service, networkProjectID, networkName, region)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
autoSubnets, err := subnetsInCIDR(subnets, autoSubnetIPRange)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(autoSubnets) == 0 {
|
||||
return "", fmt.Errorf("no subnet exists in auto CIDR")
|
||||
}
|
||||
|
||||
if len(autoSubnets) > 1 {
|
||||
return "", fmt.Errorf("multiple subnetworks in the same region exist in auto CIDR")
|
||||
}
|
||||
|
||||
return autoSubnets[0].SelfLink, nil
|
||||
}
|
||||
|
||||
func tryConvertToProjectNames(configProject, configNetworkProject string, service *compute.Service) (projID, netProjID string) {
|
||||
projID = configProject
|
||||
if isProjectNumber(projID) {
|
||||
projName, err := getProjectID(service, projID)
|
||||
if err != nil {
|
||||
glog.Warningf("Failed to retrieve project %v while trying to retrieve its name. err %v", projID, err)
|
||||
} else {
|
||||
projID = projName
|
||||
}
|
||||
}
|
||||
|
||||
netProjID = projID
|
||||
if configNetworkProject != configProject {
|
||||
netProjID = configNetworkProject
|
||||
}
|
||||
if isProjectNumber(netProjID) {
|
||||
netProjName, err := getProjectID(service, netProjID)
|
||||
if err != nil {
|
||||
glog.Warningf("Failed to retrieve network project %v while trying to retrieve its name. err %v", netProjID, err)
|
||||
} else {
|
||||
netProjID = netProjName
|
||||
}
|
||||
}
|
||||
|
||||
return projID, netProjID
|
||||
}
|
||||
|
||||
// Initialize takes in a clientBuilder and spawns a goroutine for watching the clusterid configmap.
|
||||
// This must be called before utilizing the funcs of gce.ClusterID
|
||||
func (gce *GCECloud) Initialize(clientBuilder controller.ControllerClientBuilder) {
|
||||
gce.clientBuilder = clientBuilder
|
||||
gce.client = clientBuilder.ClientOrDie("cloud-provider")
|
||||
|
||||
if gce.OnXPN() {
|
||||
gce.eventBroadcaster = record.NewBroadcaster()
|
||||
gce.eventBroadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: v1core.New(gce.client.CoreV1().RESTClient()).Events("")})
|
||||
gce.eventRecorder = gce.eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "gce-cloudprovider"})
|
||||
}
|
||||
|
||||
go gce.watchClusterID()
|
||||
}
|
||||
|
||||
// LoadBalancer returns an implementation of LoadBalancer for Google Compute Engine.
|
||||
func (gce *GCECloud) LoadBalancer() (cloudprovider.LoadBalancer, bool) {
|
||||
return gce, true
|
||||
}
|
||||
|
||||
// Instances returns an implementation of Instances for Google Compute Engine.
|
||||
func (gce *GCECloud) Instances() (cloudprovider.Instances, bool) {
|
||||
return gce, true
|
||||
}
|
||||
|
||||
// Zones returns an implementation of Zones for Google Compute Engine.
|
||||
func (gce *GCECloud) Zones() (cloudprovider.Zones, bool) {
|
||||
return gce, true
|
||||
}
|
||||
|
||||
func (gce *GCECloud) Clusters() (cloudprovider.Clusters, bool) {
|
||||
return gce, true
|
||||
}
|
||||
|
||||
// Routes returns an implementation of Routes for Google Compute Engine.
|
||||
func (gce *GCECloud) Routes() (cloudprovider.Routes, bool) {
|
||||
return gce, true
|
||||
}
|
||||
|
||||
// ProviderName returns the cloud provider ID.
|
||||
func (gce *GCECloud) ProviderName() string {
|
||||
return ProviderName
|
||||
}
|
||||
|
||||
// ProjectID returns the ProjectID corresponding to the project this cloud is in.
|
||||
func (g *GCECloud) ProjectID() string {
|
||||
return g.projectID
|
||||
}
|
||||
|
||||
// NetworkProjectID returns the ProjectID corresponding to the project this cluster's network is in.
|
||||
func (g *GCECloud) NetworkProjectID() string {
|
||||
return g.networkProjectID
|
||||
}
|
||||
|
||||
// Region returns the region
|
||||
func (gce *GCECloud) Region() string {
|
||||
return gce.region
|
||||
}
|
||||
|
||||
// OnXPN returns true if the cluster is running on a cross project network (XPN)
|
||||
func (gce *GCECloud) OnXPN() bool {
|
||||
return gce.onXPN
|
||||
}
|
||||
|
||||
// NetworkURL returns the network url
|
||||
func (gce *GCECloud) NetworkURL() string {
|
||||
return gce.networkURL
|
||||
}
|
||||
|
||||
// SubnetworkURL returns the subnetwork url
|
||||
func (gce *GCECloud) SubnetworkURL() string {
|
||||
return gce.subnetworkURL
|
||||
}
|
||||
|
||||
func (gce *GCECloud) IsLegacyNetwork() bool {
|
||||
return gce.isLegacyNetwork
|
||||
}
|
||||
|
||||
func (gce *GCECloud) SetInformers(informerFactory informers.SharedInformerFactory) {
|
||||
glog.Infof("Setting up informers for GCECloud")
|
||||
nodeInformer := informerFactory.Core().V1().Nodes().Informer()
|
||||
nodeInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
node := obj.(*v1.Node)
|
||||
gce.updateNodeZones(nil, node)
|
||||
},
|
||||
UpdateFunc: func(prev, obj interface{}) {
|
||||
prevNode := prev.(*v1.Node)
|
||||
newNode := obj.(*v1.Node)
|
||||
if newNode.Labels[kubeletapis.LabelZoneFailureDomain] ==
|
||||
prevNode.Labels[kubeletapis.LabelZoneFailureDomain] {
|
||||
return
|
||||
}
|
||||
gce.updateNodeZones(prevNode, newNode)
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
node, isNode := obj.(*v1.Node)
|
||||
// We can get DeletedFinalStateUnknown instead of *v1.Node here
|
||||
// and we need to handle that correctly.
|
||||
if !isNode {
|
||||
deletedState, ok := obj.(cache.DeletedFinalStateUnknown)
|
||||
if !ok {
|
||||
glog.Errorf("Received unexpected object: %v", obj)
|
||||
return
|
||||
}
|
||||
node, ok = deletedState.Obj.(*v1.Node)
|
||||
if !ok {
|
||||
glog.Errorf("DeletedFinalStateUnknown contained non-Node object: %v", deletedState.Obj)
|
||||
return
|
||||
}
|
||||
}
|
||||
gce.updateNodeZones(node, nil)
|
||||
},
|
||||
})
|
||||
gce.nodeInformerSynced = nodeInformer.HasSynced
|
||||
}
|
||||
|
||||
func (gce *GCECloud) updateNodeZones(prevNode, newNode *v1.Node) {
|
||||
gce.nodeZonesLock.Lock()
|
||||
defer gce.nodeZonesLock.Unlock()
|
||||
if prevNode != nil {
|
||||
prevZone, ok := prevNode.ObjectMeta.Labels[kubeletapis.LabelZoneFailureDomain]
|
||||
if ok {
|
||||
gce.nodeZones[prevZone].Delete(prevNode.ObjectMeta.Name)
|
||||
if gce.nodeZones[prevZone].Len() == 0 {
|
||||
gce.nodeZones[prevZone] = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if newNode != nil {
|
||||
newZone, ok := newNode.ObjectMeta.Labels[kubeletapis.LabelZoneFailureDomain]
|
||||
if ok {
|
||||
if gce.nodeZones[newZone] == nil {
|
||||
gce.nodeZones[newZone] = sets.NewString()
|
||||
}
|
||||
gce.nodeZones[newZone].Insert(newNode.ObjectMeta.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Known-useless DNS search path.
|
||||
var uselessDNSSearchRE = regexp.MustCompile(`^[0-9]+.google.internal.$`)
|
||||
|
||||
// ScrubDNS filters DNS settings for pods.
|
||||
func (gce *GCECloud) ScrubDNS(nameservers, searches []string) (nsOut, srchOut []string) {
|
||||
// GCE has too many search paths by default. Filter the ones we know are useless.
|
||||
for _, s := range searches {
|
||||
if !uselessDNSSearchRE.MatchString(s) {
|
||||
srchOut = append(srchOut, s)
|
||||
}
|
||||
}
|
||||
return nameservers, srchOut
|
||||
}
|
||||
|
||||
// HasClusterID returns true if the cluster has a clusterID
|
||||
func (gce *GCECloud) HasClusterID() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Project IDs cannot have a digit for the first characeter. If the id contains a digit,
|
||||
// then it must be a project number.
|
||||
func isProjectNumber(idOrNumber string) bool {
|
||||
_, err := strconv.ParseUint(idOrNumber, 10, 64)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// GCECloud implements cloudprovider.Interface.
|
||||
var _ cloudprovider.Interface = (*GCECloud)(nil)
|
||||
|
||||
func gceNetworkURL(apiEndpoint, project, network string) string {
|
||||
if apiEndpoint == "" {
|
||||
apiEndpoint = gceComputeAPIEndpoint
|
||||
}
|
||||
return apiEndpoint + strings.Join([]string{"projects", project, "global", "networks", network}, "/")
|
||||
}
|
||||
|
||||
func gceSubnetworkURL(apiEndpoint, project, region, subnetwork string) string {
|
||||
if apiEndpoint == "" {
|
||||
apiEndpoint = gceComputeAPIEndpoint
|
||||
}
|
||||
return apiEndpoint + strings.Join([]string{"projects", project, "regions", region, "subnetworks", subnetwork}, "/")
|
||||
}
|
||||
|
||||
// getProjectIDInURL parses full resource URLS and shorter URLS
|
||||
// https://www.googleapis.com/compute/v1/projects/myproject/global/networks/mycustom
|
||||
// projects/myproject/global/networks/mycustom
|
||||
// All return "myproject"
|
||||
func getProjectIDInURL(urlStr string) (string, error) {
|
||||
fields := strings.Split(urlStr, "/")
|
||||
for i, v := range fields {
|
||||
if v == "projects" && i < len(fields)-1 {
|
||||
return fields[i+1], nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("could not find project field in url: %v", urlStr)
|
||||
}
|
||||
|
||||
// getRegionInURL parses full resource URLS and shorter URLS
|
||||
// https://www.googleapis.com/compute/v1/projects/myproject/regions/us-central1/subnetworks/a
|
||||
// projects/myproject/regions/us-central1/subnetworks/a
|
||||
// All return "us-central1"
|
||||
func getRegionInURL(urlStr string) string {
|
||||
fields := strings.Split(urlStr, "/")
|
||||
for i, v := range fields {
|
||||
if v == "regions" && i < len(fields)-1 {
|
||||
return fields[i+1]
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func getNetworkNameViaMetadata() (string, error) {
|
||||
result, err := metadata.Get("instance/network-interfaces/0/network")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
parts := strings.Split(result, "/")
|
||||
if len(parts) != 4 {
|
||||
return "", fmt.Errorf("unexpected response: %s", result)
|
||||
}
|
||||
return parts[3], nil
|
||||
}
|
||||
|
||||
// getNetwork returns a GCP network
|
||||
func getNetwork(svc *compute.Service, networkProjectID, networkID string) (*compute.Network, error) {
|
||||
return svc.Networks.Get(networkProjectID, networkID).Do()
|
||||
}
|
||||
|
||||
// listSubnetworksOfNetwork returns a list of subnetworks for a particular region of a network.
|
||||
func listSubnetworksOfNetwork(svc *compute.Service, networkProjectID, networkID, region string) ([]*compute.Subnetwork, error) {
|
||||
var subnets []*compute.Subnetwork
|
||||
err := svc.Subnetworks.List(networkProjectID, region).Filter(fmt.Sprintf("network eq .*/%v$", networkID)).Pages(context.Background(), func(res *compute.SubnetworkList) error {
|
||||
subnets = append(subnets, res.Items...)
|
||||
return nil
|
||||
})
|
||||
return subnets, err
|
||||
}
|
||||
|
||||
// getProjectID returns the project's string ID given a project number or string
|
||||
func getProjectID(svc *compute.Service, projectNumberOrID string) (string, error) {
|
||||
proj, err := svc.Projects.Get(projectNumberOrID).Do()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return proj.Name, nil
|
||||
}
|
||||
|
||||
func getZonesForRegion(svc *compute.Service, projectID, region string) ([]string, error) {
|
||||
// TODO: use PageToken to list all not just the first 500
|
||||
listCall := svc.Zones.List(projectID)
|
||||
|
||||
// Filtering by region doesn't seem to work
|
||||
// (tested in https://cloud.google.com/compute/docs/reference/latest/zones/list)
|
||||
// listCall = listCall.Filter("region eq " + region)
|
||||
|
||||
res, err := listCall.Do()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unexpected response listing zones: %v", err)
|
||||
}
|
||||
zones := []string{}
|
||||
for _, zone := range res.Items {
|
||||
regionName := lastComponent(zone.Region)
|
||||
if regionName == region {
|
||||
zones = append(zones, zone.Name)
|
||||
}
|
||||
}
|
||||
return zones, nil
|
||||
}
|
||||
|
||||
func findSubnetForRegion(subnetURLs []string, region string) string {
|
||||
for _, url := range subnetURLs {
|
||||
if thisRegion := getRegionInURL(url); thisRegion == region {
|
||||
return url
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func newOauthClient(tokenSource oauth2.TokenSource) (*http.Client, error) {
|
||||
if tokenSource == nil {
|
||||
var err error
|
||||
tokenSource, err = google.DefaultTokenSource(
|
||||
oauth2.NoContext,
|
||||
compute.CloudPlatformScope,
|
||||
compute.ComputeScope)
|
||||
glog.Infof("Using DefaultTokenSource %#v", tokenSource)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
glog.Infof("Using existing Token Source %#v", tokenSource)
|
||||
}
|
||||
|
||||
if err := wait.PollImmediate(5*time.Second, 30*time.Second, func() (bool, error) {
|
||||
if _, err := tokenSource.Token(); err != nil {
|
||||
glog.Errorf("error fetching initial token: %v", err)
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return oauth2.NewClient(oauth2.NoContext, tokenSource), nil
|
||||
}
|
||||
|
||||
func (manager *gceServiceManager) getProjectsAPIEndpoint() string {
|
||||
projectsApiEndpoint := gceComputeAPIEndpoint + "projects/"
|
||||
if manager.gce.service != nil {
|
||||
projectsApiEndpoint = manager.gce.service.BasePath
|
||||
}
|
||||
|
||||
return projectsApiEndpoint
|
||||
}
|
||||
|
||||
func (manager *gceServiceManager) getProjectsAPIEndpointAlpha() string {
|
||||
projectsApiEndpoint := gceComputeAPIEndpointAlpha + "projects/"
|
||||
if manager.gce.service != nil {
|
||||
projectsApiEndpoint = manager.gce.serviceAlpha.BasePath
|
||||
}
|
||||
|
||||
return projectsApiEndpoint
|
||||
}
|
198
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_address_manager.go
generated
vendored
Normal file
198
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_address_manager.go
generated
vendored
Normal file
@ -0,0 +1,198 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gce
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
computebeta "google.golang.org/api/compute/v0.beta"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
type addressManager struct {
|
||||
logPrefix string
|
||||
svc CloudAddressService
|
||||
name string
|
||||
serviceName string
|
||||
targetIP string
|
||||
addressType lbScheme
|
||||
region string
|
||||
subnetURL string
|
||||
tryRelease bool
|
||||
}
|
||||
|
||||
func newAddressManager(svc CloudAddressService, serviceName, region, subnetURL, name, targetIP string, addressType lbScheme) *addressManager {
|
||||
return &addressManager{
|
||||
svc: svc,
|
||||
logPrefix: fmt.Sprintf("AddressManager(%q)", name),
|
||||
region: region,
|
||||
serviceName: serviceName,
|
||||
name: name,
|
||||
targetIP: targetIP,
|
||||
addressType: addressType,
|
||||
tryRelease: true,
|
||||
subnetURL: subnetURL,
|
||||
}
|
||||
}
|
||||
|
||||
// HoldAddress will ensure that the IP is reserved with an address - either owned by the controller
|
||||
// or by a user. If the address is not the addressManager.name, then it's assumed to be a user's address.
|
||||
// The string returned is the reserved IP address.
|
||||
func (am *addressManager) HoldAddress() (string, error) {
|
||||
// HoldAddress starts with retrieving the address that we use for this load balancer (by name).
|
||||
// Retrieving an address by IP will indicate if the IP is reserved and if reserved by the user
|
||||
// or the controller, but won't tell us the current state of the controller's IP. The address
|
||||
// could be reserving another address; therefore, it would need to be deleted. In the normal
|
||||
// case of using a controller address, retrieving the address by name results in the fewest API
|
||||
// calls since it indicates whether a Delete is necessary before Reserve.
|
||||
glog.V(4).Infof("%v: attempting hold of IP %q Type %q", am.logPrefix, am.targetIP, am.addressType)
|
||||
// Get the address in case it was orphaned earlier
|
||||
addr, err := am.svc.GetBetaRegionAddress(am.name, am.region)
|
||||
if err != nil && !isNotFound(err) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if addr != nil {
|
||||
// If address exists, check if the address had the expected attributes.
|
||||
validationError := am.validateAddress(addr)
|
||||
if validationError == nil {
|
||||
glog.V(4).Infof("%v: address %q already reserves IP %q Type %q. No further action required.", am.logPrefix, addr.Name, addr.Address, addr.AddressType)
|
||||
return addr.Address, nil
|
||||
}
|
||||
|
||||
glog.V(2).Infof("%v: deleting existing address because %v", am.logPrefix, validationError)
|
||||
err := am.svc.DeleteRegionAddress(addr.Name, am.region)
|
||||
if err != nil {
|
||||
if isNotFound(err) {
|
||||
glog.V(4).Infof("%v: address %q was not found. Ignoring.", am.logPrefix, addr.Name)
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
} else {
|
||||
glog.V(4).Infof("%v: successfully deleted previous address %q", am.logPrefix, addr.Name)
|
||||
}
|
||||
}
|
||||
|
||||
return am.ensureAddressReservation()
|
||||
}
|
||||
|
||||
// ReleaseAddress will release the address if it's owned by the controller.
|
||||
func (am *addressManager) ReleaseAddress() error {
|
||||
if !am.tryRelease {
|
||||
glog.V(4).Infof("%v: not attempting release of address %q.", am.logPrefix, am.targetIP)
|
||||
return nil
|
||||
}
|
||||
|
||||
glog.V(4).Infof("%v: releasing address %q named %q", am.logPrefix, am.targetIP, am.name)
|
||||
// Controller only ever tries to unreserve the address named with the load balancer's name.
|
||||
err := am.svc.DeleteRegionAddress(am.name, am.region)
|
||||
if err != nil {
|
||||
if isNotFound(err) {
|
||||
glog.Warningf("%v: address %q was not found. Ignoring.", am.logPrefix, am.name)
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
glog.V(4).Infof("%v: successfully released IP %q named %q", am.logPrefix, am.targetIP, am.name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (am *addressManager) ensureAddressReservation() (string, error) {
|
||||
// Try reserving the IP with controller-owned address name
|
||||
// If am.targetIP is an empty string, a new IP will be created.
|
||||
newAddr := &computebeta.Address{
|
||||
Name: am.name,
|
||||
Description: fmt.Sprintf(`{"kubernetes.io/service-name":"%s"}`, am.serviceName),
|
||||
Address: am.targetIP,
|
||||
AddressType: string(am.addressType),
|
||||
Subnetwork: am.subnetURL,
|
||||
}
|
||||
|
||||
reserveErr := am.svc.ReserveBetaRegionAddress(newAddr, am.region)
|
||||
if reserveErr == nil {
|
||||
if newAddr.Address != "" {
|
||||
glog.V(4).Infof("%v: successfully reserved IP %q with name %q", am.logPrefix, newAddr.Address, newAddr.Name)
|
||||
return newAddr.Address, nil
|
||||
}
|
||||
|
||||
addr, err := am.svc.GetRegionAddress(newAddr.Name, am.region)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
glog.V(4).Infof("%v: successfully created address %q which reserved IP %q", am.logPrefix, addr.Name, addr.Address)
|
||||
return addr.Address, nil
|
||||
} else if !isHTTPErrorCode(reserveErr, http.StatusConflict) && !isHTTPErrorCode(reserveErr, http.StatusBadRequest) {
|
||||
// If the IP is already reserved:
|
||||
// by an internal address: a StatusConflict is returned
|
||||
// by an external address: a BadRequest is returned
|
||||
return "", reserveErr
|
||||
}
|
||||
|
||||
// If the target IP was empty, we cannot try to find which IP caused a conflict.
|
||||
// If the name was already used, then the next sync will attempt deletion of that address.
|
||||
if am.targetIP == "" {
|
||||
return "", fmt.Errorf("failed to reserve address %q with no specific IP, err: %v", am.name, reserveErr)
|
||||
}
|
||||
|
||||
// Reserving the address failed due to a conflict or bad request. The address manager just checked that no address
|
||||
// exists with the name, so it may belong to the user.
|
||||
addr, err := am.svc.GetBetaRegionAddressByIP(am.region, am.targetIP)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get address by IP %q after reservation attempt, err: %q, reservation err: %q", am.targetIP, err, reserveErr)
|
||||
}
|
||||
|
||||
// Check that the address attributes are as required.
|
||||
if err := am.validateAddress(addr); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if am.isManagedAddress(addr) {
|
||||
// The address with this name is checked at the beginning of 'HoldAddress()', but for some reason
|
||||
// it was re-created by this point. May be possible that two controllers are running.
|
||||
glog.Warning("%v: address %q unexpectedly existed with IP %q.", am.logPrefix, addr.Name, am.targetIP)
|
||||
} else {
|
||||
// If the retrieved address is not named with the loadbalancer name, then the controller does not own it, but will allow use of it.
|
||||
glog.V(4).Infof("%v: address %q was already reserved with name: %q, description: %q", am.logPrefix, am.targetIP, addr.Name, addr.Description)
|
||||
am.tryRelease = false
|
||||
}
|
||||
|
||||
return addr.Address, nil
|
||||
}
|
||||
|
||||
func (am *addressManager) validateAddress(addr *computebeta.Address) error {
|
||||
if am.targetIP != "" && am.targetIP != addr.Address {
|
||||
return fmt.Errorf("address %q does not have the expected IP %q, actual: %q", addr.Name, am.targetIP, addr.Address)
|
||||
}
|
||||
if addr.AddressType != string(am.addressType) {
|
||||
return fmt.Errorf("address %q does not have the expected address type %q, actual: %q", addr.Name, am.addressType, addr.AddressType)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (am *addressManager) isManagedAddress(addr *computebeta.Address) bool {
|
||||
return addr.Name == am.name
|
||||
}
|
||||
|
||||
func ensureAddressDeleted(svc CloudAddressService, name, region string) error {
|
||||
return ignoreNotFound(svc.DeleteRegionAddress(name, region))
|
||||
}
|
137
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_address_manager_test.go
generated
vendored
Normal file
137
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_address_manager_test.go
generated
vendored
Normal file
@ -0,0 +1,137 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gce
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
computebeta "google.golang.org/api/compute/v0.beta"
|
||||
)
|
||||
|
||||
const testSvcName = "my-service"
|
||||
const testRegion = "us-central1"
|
||||
const testSubnet = "/projects/x/testRegions/us-central1/testSubnetworks/customsub"
|
||||
const testLBName = "a111111111111111"
|
||||
|
||||
// TestAddressManagerNoRequestedIP tests the typical case of passing in no requested IP
|
||||
func TestAddressManagerNoRequestedIP(t *testing.T) {
|
||||
svc := NewFakeCloudAddressService()
|
||||
targetIP := ""
|
||||
|
||||
mgr := newAddressManager(svc, testSvcName, testRegion, testSubnet, testLBName, targetIP, schemeInternal)
|
||||
testHoldAddress(t, mgr, svc, testLBName, testRegion, targetIP, string(schemeInternal))
|
||||
testReleaseAddress(t, mgr, svc, testLBName, testRegion)
|
||||
}
|
||||
|
||||
// TestAddressManagerBasic tests the typical case of reserving and unreserving an address.
|
||||
func TestAddressManagerBasic(t *testing.T) {
|
||||
svc := NewFakeCloudAddressService()
|
||||
targetIP := "1.1.1.1"
|
||||
|
||||
mgr := newAddressManager(svc, testSvcName, testRegion, testSubnet, testLBName, targetIP, schemeInternal)
|
||||
testHoldAddress(t, mgr, svc, testLBName, testRegion, targetIP, string(schemeInternal))
|
||||
testReleaseAddress(t, mgr, svc, testLBName, testRegion)
|
||||
}
|
||||
|
||||
// TestAddressManagerOrphaned tests the case where the address exists with the IP being equal
|
||||
// to the requested address (forwarding rule or loadbalancer IP).
|
||||
func TestAddressManagerOrphaned(t *testing.T) {
|
||||
svc := NewFakeCloudAddressService()
|
||||
targetIP := "1.1.1.1"
|
||||
|
||||
addr := &computebeta.Address{Name: testLBName, Address: targetIP, AddressType: string(schemeInternal)}
|
||||
err := svc.ReserveBetaRegionAddress(addr, testRegion)
|
||||
require.NoError(t, err)
|
||||
|
||||
mgr := newAddressManager(svc, testSvcName, testRegion, testSubnet, testLBName, targetIP, schemeInternal)
|
||||
testHoldAddress(t, mgr, svc, testLBName, testRegion, targetIP, string(schemeInternal))
|
||||
testReleaseAddress(t, mgr, svc, testLBName, testRegion)
|
||||
}
|
||||
|
||||
// TestAddressManagerOutdatedOrphan tests the case where an address exists but points to
|
||||
// an IP other than the forwarding rule or loadbalancer IP.
|
||||
func TestAddressManagerOutdatedOrphan(t *testing.T) {
|
||||
svc := NewFakeCloudAddressService()
|
||||
previousAddress := "1.1.0.0"
|
||||
targetIP := "1.1.1.1"
|
||||
|
||||
addr := &computebeta.Address{Name: testLBName, Address: previousAddress, AddressType: string(schemeExternal)}
|
||||
err := svc.ReserveBetaRegionAddress(addr, testRegion)
|
||||
require.NoError(t, err)
|
||||
|
||||
mgr := newAddressManager(svc, testSvcName, testRegion, testSubnet, testLBName, targetIP, schemeInternal)
|
||||
testHoldAddress(t, mgr, svc, testLBName, testRegion, targetIP, string(schemeInternal))
|
||||
testReleaseAddress(t, mgr, svc, testLBName, testRegion)
|
||||
}
|
||||
|
||||
// TestAddressManagerExternallyOwned tests the case where the address exists but isn't
|
||||
// owned by the controller.
|
||||
func TestAddressManagerExternallyOwned(t *testing.T) {
|
||||
svc := NewFakeCloudAddressService()
|
||||
targetIP := "1.1.1.1"
|
||||
|
||||
addr := &computebeta.Address{Name: "my-important-address", Address: targetIP, AddressType: string(schemeInternal)}
|
||||
err := svc.ReserveBetaRegionAddress(addr, testRegion)
|
||||
require.NoError(t, err)
|
||||
|
||||
mgr := newAddressManager(svc, testSvcName, testRegion, testSubnet, testLBName, targetIP, schemeInternal)
|
||||
ipToUse, err := mgr.HoldAddress()
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, ipToUse)
|
||||
|
||||
_, err = svc.GetRegionAddress(testLBName, testRegion)
|
||||
assert.True(t, isNotFound(err))
|
||||
|
||||
testReleaseAddress(t, mgr, svc, testLBName, testRegion)
|
||||
}
|
||||
|
||||
// TestAddressManagerExternallyOwned tests the case where the address exists but isn't
|
||||
// owned by the controller. However, this address has the wrong type.
|
||||
func TestAddressManagerBadExternallyOwned(t *testing.T) {
|
||||
svc := NewFakeCloudAddressService()
|
||||
targetIP := "1.1.1.1"
|
||||
|
||||
addr := &computebeta.Address{Name: "my-important-address", Address: targetIP, AddressType: string(schemeExternal)}
|
||||
err := svc.ReserveBetaRegionAddress(addr, testRegion)
|
||||
require.NoError(t, err)
|
||||
|
||||
mgr := newAddressManager(svc, testSvcName, testRegion, testSubnet, testLBName, targetIP, schemeInternal)
|
||||
_, err = mgr.HoldAddress()
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func testHoldAddress(t *testing.T, mgr *addressManager, svc CloudAddressService, name, region, targetIP, scheme string) {
|
||||
ipToUse, err := mgr.HoldAddress()
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, ipToUse)
|
||||
|
||||
addr, err := svc.GetBetaRegionAddress(name, region)
|
||||
require.NoError(t, err)
|
||||
if targetIP != "" {
|
||||
assert.EqualValues(t, targetIP, addr.Address)
|
||||
}
|
||||
assert.EqualValues(t, scheme, addr.AddressType)
|
||||
}
|
||||
|
||||
func testReleaseAddress(t *testing.T, mgr *addressManager, svc CloudAddressService, name, region string) {
|
||||
err := mgr.ReleaseAddress()
|
||||
require.NoError(t, err)
|
||||
_, err = svc.GetBetaRegionAddress(name, region)
|
||||
assert.True(t, isNotFound(err))
|
||||
}
|
190
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_addresses.go
generated
vendored
Normal file
190
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_addresses.go
generated
vendored
Normal file
@ -0,0 +1,190 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gce
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
computealpha "google.golang.org/api/compute/v0.alpha"
|
||||
computebeta "google.golang.org/api/compute/v0.beta"
|
||||
compute "google.golang.org/api/compute/v1"
|
||||
)
|
||||
|
||||
func newAddressMetricContext(request, region string) *metricContext {
|
||||
return newAddressMetricContextWithVersion(request, region, computeV1Version)
|
||||
}
|
||||
|
||||
func newAddressMetricContextWithVersion(request, region, version string) *metricContext {
|
||||
return newGenericMetricContext("address", request, region, unusedMetricLabel, version)
|
||||
}
|
||||
|
||||
// ReserveGlobalAddress creates a global address.
|
||||
// Caller is allocated a random IP if they do not specify an ipAddress. If an
|
||||
// ipAddress is specified, it must belong to the current project, eg: an
|
||||
// ephemeral IP associated with a global forwarding rule.
|
||||
func (gce *GCECloud) ReserveGlobalAddress(addr *compute.Address) error {
|
||||
mc := newAddressMetricContext("reserve", "")
|
||||
op, err := gce.service.GlobalAddresses.Insert(gce.projectID, addr).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
return gce.waitForGlobalOp(op, mc)
|
||||
}
|
||||
|
||||
// DeleteGlobalAddress deletes a global address by name.
|
||||
func (gce *GCECloud) DeleteGlobalAddress(name string) error {
|
||||
mc := newAddressMetricContext("delete", "")
|
||||
op, err := gce.service.GlobalAddresses.Delete(gce.projectID, name).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
return gce.waitForGlobalOp(op, mc)
|
||||
}
|
||||
|
||||
// GetGlobalAddress returns the global address by name.
|
||||
func (gce *GCECloud) GetGlobalAddress(name string) (*compute.Address, error) {
|
||||
mc := newAddressMetricContext("get", "")
|
||||
v, err := gce.service.GlobalAddresses.Get(gce.projectID, name).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
||||
|
||||
// ReserveRegionAddress creates a region address
|
||||
func (gce *GCECloud) ReserveRegionAddress(addr *compute.Address, region string) error {
|
||||
mc := newAddressMetricContext("reserve", region)
|
||||
op, err := gce.service.Addresses.Insert(gce.projectID, region, addr).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
return gce.waitForRegionOp(op, region, mc)
|
||||
}
|
||||
|
||||
// ReserveAlphaRegionAddress creates an Alpha, regional address.
|
||||
func (gce *GCECloud) ReserveAlphaRegionAddress(addr *computealpha.Address, region string) error {
|
||||
mc := newAddressMetricContextWithVersion("reserve", region, computeAlphaVersion)
|
||||
op, err := gce.serviceAlpha.Addresses.Insert(gce.projectID, region, addr).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
return gce.waitForRegionOp(op, region, mc)
|
||||
}
|
||||
|
||||
// ReserveBetaRegionAddress creates a beta region address
|
||||
func (gce *GCECloud) ReserveBetaRegionAddress(addr *computebeta.Address, region string) error {
|
||||
mc := newAddressMetricContextWithVersion("reserve", region, computeBetaVersion)
|
||||
op, err := gce.serviceBeta.Addresses.Insert(gce.projectID, region, addr).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
return gce.waitForRegionOp(op, region, mc)
|
||||
}
|
||||
|
||||
// DeleteRegionAddress deletes a region address by name.
|
||||
func (gce *GCECloud) DeleteRegionAddress(name, region string) error {
|
||||
mc := newAddressMetricContext("delete", region)
|
||||
op, err := gce.service.Addresses.Delete(gce.projectID, region, name).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
return gce.waitForRegionOp(op, region, mc)
|
||||
}
|
||||
|
||||
// GetRegionAddress returns the region address by name
|
||||
func (gce *GCECloud) GetRegionAddress(name, region string) (*compute.Address, error) {
|
||||
mc := newAddressMetricContext("get", region)
|
||||
v, err := gce.service.Addresses.Get(gce.projectID, region, name).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
||||
|
||||
// GetAlphaRegionAddress returns the Alpha, regional address by name.
|
||||
func (gce *GCECloud) GetAlphaRegionAddress(name, region string) (*computealpha.Address, error) {
|
||||
mc := newAddressMetricContextWithVersion("get", region, computeAlphaVersion)
|
||||
v, err := gce.serviceAlpha.Addresses.Get(gce.projectID, region, name).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
||||
|
||||
// GetBetaRegionAddress returns the beta region address by name
|
||||
func (gce *GCECloud) GetBetaRegionAddress(name, region string) (*computebeta.Address, error) {
|
||||
mc := newAddressMetricContextWithVersion("get", region, computeBetaVersion)
|
||||
v, err := gce.serviceBeta.Addresses.Get(gce.projectID, region, name).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
||||
|
||||
// GetRegionAddressByIP returns the regional address matching the given IP address.
|
||||
func (gce *GCECloud) GetRegionAddressByIP(region, ipAddress string) (*compute.Address, error) {
|
||||
mc := newAddressMetricContext("list", region)
|
||||
addrs, err := gce.service.Addresses.List(gce.projectID, region).Filter("address eq " + ipAddress).Do()
|
||||
// Record the metrics for the call.
|
||||
mc.Observe(err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(addrs.Items) > 1 {
|
||||
// We don't expect more than one match.
|
||||
addrsToPrint := []compute.Address{}
|
||||
for _, addr := range addrs.Items {
|
||||
addrsToPrint = append(addrsToPrint, *addr)
|
||||
}
|
||||
glog.Errorf("More than one addresses matching the IP %q: %+v", ipAddress, addrsToPrint)
|
||||
}
|
||||
for _, addr := range addrs.Items {
|
||||
if addr.Address == ipAddress {
|
||||
return addr, nil
|
||||
}
|
||||
}
|
||||
return nil, makeGoogleAPINotFoundError(fmt.Sprintf("Address with IP %q was not found in region %q", ipAddress, region))
|
||||
}
|
||||
|
||||
// GetBetaRegionAddressByIP returns the beta regional address matching the given IP address.
|
||||
func (gce *GCECloud) GetBetaRegionAddressByIP(region, ipAddress string) (*computebeta.Address, error) {
|
||||
mc := newAddressMetricContext("list", region)
|
||||
addrs, err := gce.serviceBeta.Addresses.List(gce.projectID, region).Filter("address eq " + ipAddress).Do()
|
||||
// Record the metrics for the call.
|
||||
mc.Observe(err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(addrs.Items) > 1 {
|
||||
// We don't expect more than one match.
|
||||
addrsToPrint := []computebeta.Address{}
|
||||
for _, addr := range addrs.Items {
|
||||
addrsToPrint = append(addrsToPrint, *addr)
|
||||
}
|
||||
glog.Errorf("More than one addresses matching the IP %q: %+v", ipAddress, addrsToPrint)
|
||||
}
|
||||
for _, addr := range addrs.Items {
|
||||
if addr.Address == ipAddress {
|
||||
return addr, nil
|
||||
}
|
||||
}
|
||||
return nil, makeGoogleAPINotFoundError(fmt.Sprintf("Address with IP %q was not found in region %q", ipAddress, region))
|
||||
}
|
||||
|
||||
// TODO(#51665): retire this function once Network Tiers becomes Beta in GCP.
|
||||
func (gce *GCECloud) getNetworkTierFromAddress(name, region string) (string, error) {
|
||||
if !gce.AlphaFeatureGate.Enabled(AlphaFeatureNetworkTiers) {
|
||||
return NetworkTierDefault.ToGCEValue(), nil
|
||||
}
|
||||
addr, err := gce.GetAlphaRegionAddress(name, region)
|
||||
if err != nil {
|
||||
return handleAlphaNetworkTierGetError(err)
|
||||
}
|
||||
return addr.NetworkTier, nil
|
||||
}
|
237
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_addresses_fakes.go
generated
vendored
Normal file
237
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_addresses_fakes.go
generated
vendored
Normal file
@ -0,0 +1,237 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gce
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
computealpha "google.golang.org/api/compute/v0.alpha"
|
||||
computebeta "google.golang.org/api/compute/v0.beta"
|
||||
compute "google.golang.org/api/compute/v1"
|
||||
)
|
||||
|
||||
// test
|
||||
|
||||
type FakeCloudAddressService struct {
|
||||
count int
|
||||
// reservedAddrs tracks usage of IP addresses
|
||||
// Key is the IP address as a string
|
||||
reservedAddrs map[string]bool
|
||||
// addrsByRegionAndName
|
||||
// Outer key is for region string; inner key is for address name.
|
||||
addrsByRegionAndName map[string]map[string]*computealpha.Address
|
||||
}
|
||||
|
||||
// FakeCloudAddressService Implements CloudAddressService
|
||||
var _ CloudAddressService = &FakeCloudAddressService{}
|
||||
|
||||
func NewFakeCloudAddressService() *FakeCloudAddressService {
|
||||
return &FakeCloudAddressService{
|
||||
reservedAddrs: make(map[string]bool),
|
||||
addrsByRegionAndName: make(map[string]map[string]*computealpha.Address),
|
||||
}
|
||||
}
|
||||
|
||||
// SetRegionalAddresses sets the addresses of ther region. This is used for
|
||||
// setting the test environment.
|
||||
func (cas *FakeCloudAddressService) SetRegionalAddresses(region string, addrs []*computealpha.Address) {
|
||||
// Reset addresses in the region.
|
||||
cas.addrsByRegionAndName[region] = make(map[string]*computealpha.Address)
|
||||
|
||||
for _, addr := range addrs {
|
||||
cas.reservedAddrs[addr.Address] = true
|
||||
cas.addrsByRegionAndName[region][addr.Name] = addr
|
||||
}
|
||||
}
|
||||
|
||||
func (cas *FakeCloudAddressService) ReserveAlphaRegionAddress(addr *computealpha.Address, region string) error {
|
||||
if addr.Address == "" {
|
||||
addr.Address = fmt.Sprintf("1.2.3.%d", cas.count)
|
||||
cas.count++
|
||||
}
|
||||
|
||||
if addr.AddressType == "" {
|
||||
addr.AddressType = string(schemeExternal)
|
||||
}
|
||||
|
||||
if cas.reservedAddrs[addr.Address] {
|
||||
msg := "IP in use"
|
||||
// When the IP is already in use, this call returns an error code based
|
||||
// on the type (internal vs external) of the address. This is to be
|
||||
// consistent with actual GCE API.
|
||||
switch lbScheme(addr.AddressType) {
|
||||
case schemeExternal:
|
||||
return makeGoogleAPIError(http.StatusBadRequest, msg)
|
||||
default:
|
||||
return makeGoogleAPIError(http.StatusConflict, msg)
|
||||
}
|
||||
}
|
||||
|
||||
if _, exists := cas.addrsByRegionAndName[region]; !exists {
|
||||
cas.addrsByRegionAndName[region] = make(map[string]*computealpha.Address)
|
||||
}
|
||||
|
||||
if _, exists := cas.addrsByRegionAndName[region][addr.Name]; exists {
|
||||
return makeGoogleAPIError(http.StatusConflict, "name in use")
|
||||
}
|
||||
|
||||
cas.addrsByRegionAndName[region][addr.Name] = addr
|
||||
cas.reservedAddrs[addr.Address] = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cas *FakeCloudAddressService) ReserveBetaRegionAddress(addr *computebeta.Address, region string) error {
|
||||
alphaAddr := convertToAlphaAddress(addr)
|
||||
return cas.ReserveAlphaRegionAddress(alphaAddr, region)
|
||||
}
|
||||
|
||||
func (cas *FakeCloudAddressService) ReserveRegionAddress(addr *compute.Address, region string) error {
|
||||
alphaAddr := convertToAlphaAddress(addr)
|
||||
return cas.ReserveAlphaRegionAddress(alphaAddr, region)
|
||||
}
|
||||
|
||||
func (cas *FakeCloudAddressService) GetAlphaRegionAddress(name, region string) (*computealpha.Address, error) {
|
||||
if _, exists := cas.addrsByRegionAndName[region]; !exists {
|
||||
return nil, makeGoogleAPINotFoundError("")
|
||||
}
|
||||
|
||||
if addr, exists := cas.addrsByRegionAndName[region][name]; !exists {
|
||||
return nil, makeGoogleAPINotFoundError("")
|
||||
} else {
|
||||
return addr, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (cas *FakeCloudAddressService) GetBetaRegionAddress(name, region string) (*computebeta.Address, error) {
|
||||
addr, err := cas.GetAlphaRegionAddress(name, region)
|
||||
if addr != nil {
|
||||
return convertToBetaAddress(addr), err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (cas *FakeCloudAddressService) GetRegionAddress(name, region string) (*compute.Address, error) {
|
||||
addr, err := cas.GetAlphaRegionAddress(name, region)
|
||||
if addr != nil {
|
||||
return convertToV1Address(addr), err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (cas *FakeCloudAddressService) DeleteRegionAddress(name, region string) error {
|
||||
if _, exists := cas.addrsByRegionAndName[region]; !exists {
|
||||
return makeGoogleAPINotFoundError("")
|
||||
}
|
||||
|
||||
addr, exists := cas.addrsByRegionAndName[region][name]
|
||||
if !exists {
|
||||
return makeGoogleAPINotFoundError("")
|
||||
}
|
||||
|
||||
delete(cas.reservedAddrs, addr.Address)
|
||||
delete(cas.addrsByRegionAndName[region], name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cas *FakeCloudAddressService) GetAlphaRegionAddressByIP(region, ipAddress string) (*computealpha.Address, error) {
|
||||
if _, exists := cas.addrsByRegionAndName[region]; !exists {
|
||||
return nil, makeGoogleAPINotFoundError("")
|
||||
}
|
||||
|
||||
for _, addr := range cas.addrsByRegionAndName[region] {
|
||||
if addr.Address == ipAddress {
|
||||
return addr, nil
|
||||
}
|
||||
}
|
||||
return nil, makeGoogleAPINotFoundError("")
|
||||
}
|
||||
|
||||
func (cas *FakeCloudAddressService) GetBetaRegionAddressByIP(name, region string) (*computebeta.Address, error) {
|
||||
addr, err := cas.GetAlphaRegionAddressByIP(name, region)
|
||||
if addr != nil {
|
||||
return convertToBetaAddress(addr), nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (cas *FakeCloudAddressService) GetRegionAddressByIP(name, region string) (*compute.Address, error) {
|
||||
addr, err := cas.GetAlphaRegionAddressByIP(name, region)
|
||||
if addr != nil {
|
||||
return convertToV1Address(addr), nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (cas *FakeCloudAddressService) getNetworkTierFromAddress(name, region string) (string, error) {
|
||||
addr, err := cas.GetAlphaRegionAddress(name, region)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return addr.NetworkTier, nil
|
||||
}
|
||||
|
||||
func convertToV1Address(object gceObject) *compute.Address {
|
||||
enc, err := object.MarshalJSON()
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Failed to encode to json: %v", err))
|
||||
}
|
||||
var addr compute.Address
|
||||
if err := json.Unmarshal(enc, &addr); err != nil {
|
||||
panic(fmt.Sprintf("Failed to convert GCE apiObject %v to v1 address: %v", object, err))
|
||||
}
|
||||
return &addr
|
||||
}
|
||||
|
||||
func convertToAlphaAddress(object gceObject) *computealpha.Address {
|
||||
enc, err := object.MarshalJSON()
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Failed to encode to json: %v", err))
|
||||
}
|
||||
var addr computealpha.Address
|
||||
if err := json.Unmarshal(enc, &addr); err != nil {
|
||||
panic(fmt.Sprintf("Failed to convert GCE apiObject %v to alpha address: %v", object, err))
|
||||
}
|
||||
// Set the default values for the Alpha fields.
|
||||
addr.NetworkTier = NetworkTierDefault.ToGCEValue()
|
||||
return &addr
|
||||
}
|
||||
|
||||
func convertToBetaAddress(object gceObject) *computebeta.Address {
|
||||
enc, err := object.MarshalJSON()
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Failed to encode to json: %v", err))
|
||||
}
|
||||
var addr computebeta.Address
|
||||
if err := json.Unmarshal(enc, &addr); err != nil {
|
||||
panic(fmt.Sprintf("Failed to convert GCE apiObject %v to beta address: %v", object, err))
|
||||
}
|
||||
return &addr
|
||||
}
|
||||
|
||||
func (cas *FakeCloudAddressService) String() string {
|
||||
var b bytes.Buffer
|
||||
for region, regAddresses := range cas.addrsByRegionAndName {
|
||||
b.WriteString(fmt.Sprintf("%v:\n", region))
|
||||
for name, addr := range regAddresses {
|
||||
b.WriteString(fmt.Sprintf(" %v: %v\n", name, addr.Address))
|
||||
}
|
||||
}
|
||||
return b.String()
|
||||
}
|
70
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_alpha.go
generated
vendored
Normal file
70
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_alpha.go
generated
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gce
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
// alpha: v1.8 (for Services)
|
||||
//
|
||||
// Allows Services backed by a GCP load balancer to choose what network
|
||||
// tier to use. Currently supports "Standard" and "Premium" (default).
|
||||
AlphaFeatureNetworkTiers = "NetworkTiers"
|
||||
|
||||
AlphaFeatureGCEDisk = "DiskAlphaAPI"
|
||||
|
||||
AlphaFeatureNetworkEndpointGroup = "NetworkEndpointGroup"
|
||||
)
|
||||
|
||||
// All known alpha features
|
||||
var knownAlphaFeatures = map[string]bool{
|
||||
AlphaFeatureNetworkTiers: true,
|
||||
AlphaFeatureGCEDisk: true,
|
||||
AlphaFeatureNetworkEndpointGroup: true,
|
||||
}
|
||||
|
||||
type AlphaFeatureGate struct {
|
||||
features map[string]bool
|
||||
}
|
||||
|
||||
func (af *AlphaFeatureGate) Enabled(key string) bool {
|
||||
return af.features[key]
|
||||
}
|
||||
|
||||
func NewAlphaFeatureGate(features []string) (*AlphaFeatureGate, error) {
|
||||
errList := []error{}
|
||||
featureMap := make(map[string]bool)
|
||||
for _, name := range features {
|
||||
if _, ok := knownAlphaFeatures[name]; !ok {
|
||||
errList = append(errList, fmt.Errorf("alpha feature %q is not supported.", name))
|
||||
} else {
|
||||
featureMap[name] = true
|
||||
}
|
||||
}
|
||||
return &AlphaFeatureGate{featureMap}, utilerrors.NewAggregate(errList)
|
||||
}
|
||||
|
||||
func (gce *GCECloud) alphaFeatureEnabled(feature string) error {
|
||||
if !gce.AlphaFeatureGate.Enabled(feature) {
|
||||
return fmt.Errorf("alpha feature %q is not enabled.", feature)
|
||||
}
|
||||
return nil
|
||||
}
|
134
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_annotations.go
generated
vendored
Normal file
134
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_annotations.go
generated
vendored
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gce
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
type LoadBalancerType string
|
||||
type NetworkTier string
|
||||
|
||||
const (
|
||||
// ServiceAnnotationLoadBalancerType is annotated on a service with type LoadBalancer
|
||||
// dictates what specific kind of GCP LB should be assembled.
|
||||
// Currently, only "internal" is supported.
|
||||
ServiceAnnotationLoadBalancerType = "cloud.google.com/load-balancer-type"
|
||||
|
||||
LBTypeInternal LoadBalancerType = "Internal"
|
||||
// Deprecating the lowercase spelling of Internal.
|
||||
deprecatedTypeInternalLowerCase LoadBalancerType = "internal"
|
||||
|
||||
// ServiceAnnotationInternalBackendShare is annotated on a service with "true" when users
|
||||
// want to share GCP Backend Services for a set of internal load balancers.
|
||||
// ALPHA feature - this may be removed in a future release.
|
||||
ServiceAnnotationILBBackendShare = "alpha.cloud.google.com/load-balancer-backend-share"
|
||||
// This annotation did not correctly specify "alpha", so both annotations will be checked.
|
||||
deprecatedServiceAnnotationILBBackendShare = "cloud.google.com/load-balancer-backend-share"
|
||||
|
||||
// NetworkTierAnnotationKey is annotated on a Service object to indicate which
|
||||
// network tier a GCP LB should use. The valid values are "Standard" and
|
||||
// "Premium" (default).
|
||||
NetworkTierAnnotationKey = "cloud.google.com/network-tier"
|
||||
NetworkTierAnnotationStandard = "Standard"
|
||||
NetworkTierAnnotationPremium = "Premium"
|
||||
|
||||
NetworkTierStandard NetworkTier = NetworkTierAnnotationStandard
|
||||
NetworkTierPremium NetworkTier = NetworkTierAnnotationPremium
|
||||
NetworkTierDefault NetworkTier = NetworkTierPremium
|
||||
)
|
||||
|
||||
// GetLoadBalancerAnnotationType returns the type of GCP load balancer which should be assembled.
|
||||
func GetLoadBalancerAnnotationType(service *v1.Service) (LoadBalancerType, bool) {
|
||||
v := LoadBalancerType("")
|
||||
if service.Spec.Type != v1.ServiceTypeLoadBalancer {
|
||||
return v, false
|
||||
}
|
||||
|
||||
l, ok := service.Annotations[ServiceAnnotationLoadBalancerType]
|
||||
v = LoadBalancerType(l)
|
||||
if !ok {
|
||||
return v, false
|
||||
}
|
||||
|
||||
switch v {
|
||||
case LBTypeInternal, deprecatedTypeInternalLowerCase:
|
||||
return LBTypeInternal, true
|
||||
default:
|
||||
return v, false
|
||||
}
|
||||
}
|
||||
|
||||
// GetLoadBalancerAnnotationBackendShare returns whether this service's backend service should be
|
||||
// shared with other load balancers. Health checks and the healthcheck firewall will be shared regardless.
|
||||
func GetLoadBalancerAnnotationBackendShare(service *v1.Service) bool {
|
||||
if l, exists := service.Annotations[ServiceAnnotationILBBackendShare]; exists && l == "true" {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check for deprecated annotation key
|
||||
if l, exists := service.Annotations[deprecatedServiceAnnotationILBBackendShare]; exists && l == "true" {
|
||||
glog.Warningf("Annotation %q is deprecated and replaced with an alpha-specific key: %q", deprecatedServiceAnnotationILBBackendShare, ServiceAnnotationILBBackendShare)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// GetServiceNetworkTier returns the network tier of GCP load balancer
|
||||
// which should be assembled, and an error if the specified tier is not
|
||||
// supported.
|
||||
func GetServiceNetworkTier(service *v1.Service) (NetworkTier, error) {
|
||||
l, ok := service.Annotations[NetworkTierAnnotationKey]
|
||||
if !ok {
|
||||
return NetworkTierDefault, nil
|
||||
}
|
||||
|
||||
v := NetworkTier(l)
|
||||
switch v {
|
||||
case NetworkTierStandard:
|
||||
fallthrough
|
||||
case NetworkTierPremium:
|
||||
return v, nil
|
||||
default:
|
||||
return NetworkTierDefault, fmt.Errorf("unsupported network tier: %q", v)
|
||||
}
|
||||
}
|
||||
|
||||
// ToGCEValue converts NetworkTier to a string that we can populate the
|
||||
// NetworkTier field of GCE objects.
|
||||
func (n NetworkTier) ToGCEValue() string {
|
||||
return strings.ToUpper(string(n))
|
||||
}
|
||||
|
||||
// NetworkTierGCEValueToType converts the value of the NetworkTier field of a
|
||||
// GCE object to the NetworkTier type.
|
||||
func NetworkTierGCEValueToType(s string) NetworkTier {
|
||||
switch s {
|
||||
case "STANDARD":
|
||||
return NetworkTierStandard
|
||||
case "PREMIUM":
|
||||
return NetworkTierPremium
|
||||
default:
|
||||
return NetworkTier(s)
|
||||
}
|
||||
}
|
70
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_annotations_test.go
generated
vendored
Normal file
70
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_annotations_test.go
generated
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gce
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestServiceNetworkTierAnnotationKey(t *testing.T) {
|
||||
createTestService := func() *v1.Service {
|
||||
return &v1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
UID: "randome-uid",
|
||||
Name: "test-svc",
|
||||
Namespace: "test-ns",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
for testName, testCase := range map[string]struct {
|
||||
annotations map[string]string
|
||||
expectedTier NetworkTier
|
||||
expectErr bool
|
||||
}{
|
||||
"Use the default when the annotation does not exist": {
|
||||
annotations: nil,
|
||||
expectedTier: NetworkTierDefault,
|
||||
},
|
||||
"Standard tier": {
|
||||
annotations: map[string]string{NetworkTierAnnotationKey: "Standard"},
|
||||
expectedTier: NetworkTierStandard,
|
||||
},
|
||||
"Premium tier": {
|
||||
annotations: map[string]string{NetworkTierAnnotationKey: "Premium"},
|
||||
expectedTier: NetworkTierPremium,
|
||||
},
|
||||
"Report an error on invalid network tier value": {
|
||||
annotations: map[string]string{NetworkTierAnnotationKey: "Unknown-tier"},
|
||||
expectedTier: NetworkTierPremium,
|
||||
expectErr: true,
|
||||
},
|
||||
} {
|
||||
t.Run(testName, func(t *testing.T) {
|
||||
svc := createTestService()
|
||||
svc.Annotations = testCase.annotations
|
||||
actualTier, err := GetServiceNetworkTier(svc)
|
||||
assert.Equal(t, testCase.expectedTier, actualTier)
|
||||
assert.Equal(t, testCase.expectErr, err != nil)
|
||||
})
|
||||
}
|
||||
}
|
183
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_backendservice.go
generated
vendored
Normal file
183
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_backendservice.go
generated
vendored
Normal file
@ -0,0 +1,183 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gce
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
computealpha "google.golang.org/api/compute/v0.alpha"
|
||||
compute "google.golang.org/api/compute/v1"
|
||||
)
|
||||
|
||||
func newBackendServiceMetricContext(request, region string) *metricContext {
|
||||
return newBackendServiceMetricContextWithVersion(request, region, computeV1Version)
|
||||
}
|
||||
|
||||
func newBackendServiceMetricContextWithVersion(request, region, version string) *metricContext {
|
||||
return newGenericMetricContext("backendservice", request, region, unusedMetricLabel, version)
|
||||
}
|
||||
|
||||
// GetGlobalBackendService retrieves a backend by name.
|
||||
func (gce *GCECloud) GetGlobalBackendService(name string) (*compute.BackendService, error) {
|
||||
mc := newBackendServiceMetricContext("get", "")
|
||||
v, err := gce.service.BackendServices.Get(gce.projectID, name).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
||||
|
||||
// GetAlphaGlobalBackendService retrieves alpha backend by name.
|
||||
func (gce *GCECloud) GetAlphaGlobalBackendService(name string) (*computealpha.BackendService, error) {
|
||||
mc := newBackendServiceMetricContextWithVersion("get", "", computeAlphaVersion)
|
||||
v, err := gce.serviceAlpha.BackendServices.Get(gce.projectID, name).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
||||
|
||||
// UpdateGlobalBackendService applies the given BackendService as an update to an existing service.
|
||||
func (gce *GCECloud) UpdateGlobalBackendService(bg *compute.BackendService) error {
|
||||
mc := newBackendServiceMetricContext("update", "")
|
||||
op, err := gce.service.BackendServices.Update(gce.projectID, bg.Name, bg).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
|
||||
return gce.waitForGlobalOp(op, mc)
|
||||
}
|
||||
|
||||
// UpdateAlphaGlobalBackendService applies the given alpha BackendService as an update to an existing service.
|
||||
func (gce *GCECloud) UpdateAlphaGlobalBackendService(bg *computealpha.BackendService) error {
|
||||
mc := newBackendServiceMetricContextWithVersion("update", "", computeAlphaVersion)
|
||||
op, err := gce.serviceAlpha.BackendServices.Update(gce.projectID, bg.Name, bg).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
|
||||
return gce.waitForGlobalOp(op, mc)
|
||||
}
|
||||
|
||||
// DeleteGlobalBackendService deletes the given BackendService by name.
|
||||
func (gce *GCECloud) DeleteGlobalBackendService(name string) error {
|
||||
mc := newBackendServiceMetricContext("delete", "")
|
||||
op, err := gce.service.BackendServices.Delete(gce.projectID, name).Do()
|
||||
if err != nil {
|
||||
if isHTTPErrorCode(err, http.StatusNotFound) {
|
||||
return nil
|
||||
}
|
||||
return mc.Observe(err)
|
||||
}
|
||||
|
||||
return gce.waitForGlobalOp(op, mc)
|
||||
}
|
||||
|
||||
// CreateGlobalBackendService creates the given BackendService.
|
||||
func (gce *GCECloud) CreateGlobalBackendService(bg *compute.BackendService) error {
|
||||
mc := newBackendServiceMetricContext("create", "")
|
||||
op, err := gce.service.BackendServices.Insert(gce.projectID, bg).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
|
||||
return gce.waitForGlobalOp(op, mc)
|
||||
}
|
||||
|
||||
// CreateAlphaGlobalBackendService creates the given alpha BackendService.
|
||||
func (gce *GCECloud) CreateAlphaGlobalBackendService(bg *computealpha.BackendService) error {
|
||||
mc := newBackendServiceMetricContextWithVersion("create", "", computeAlphaVersion)
|
||||
op, err := gce.serviceAlpha.BackendServices.Insert(gce.projectID, bg).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
|
||||
return gce.waitForGlobalOp(op, mc)
|
||||
}
|
||||
|
||||
// ListGlobalBackendServices lists all backend services in the project.
|
||||
func (gce *GCECloud) ListGlobalBackendServices() (*compute.BackendServiceList, error) {
|
||||
mc := newBackendServiceMetricContext("list", "")
|
||||
// TODO: use PageToken to list all not just the first 500
|
||||
v, err := gce.service.BackendServices.List(gce.projectID).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
||||
|
||||
// GetGlobalBackendServiceHealth returns the health of the BackendService identified by the given
|
||||
// name, in the given instanceGroup. The instanceGroupLink is the fully
|
||||
// qualified self link of an instance group.
|
||||
func (gce *GCECloud) GetGlobalBackendServiceHealth(name string, instanceGroupLink string) (*compute.BackendServiceGroupHealth, error) {
|
||||
mc := newBackendServiceMetricContext("get_health", "")
|
||||
groupRef := &compute.ResourceGroupReference{Group: instanceGroupLink}
|
||||
v, err := gce.service.BackendServices.GetHealth(gce.projectID, name, groupRef).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
||||
|
||||
// GetRegionBackendService retrieves a backend by name.
|
||||
func (gce *GCECloud) GetRegionBackendService(name, region string) (*compute.BackendService, error) {
|
||||
mc := newBackendServiceMetricContext("get", region)
|
||||
v, err := gce.service.RegionBackendServices.Get(gce.projectID, region, name).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
||||
|
||||
// UpdateRegionBackendService applies the given BackendService as an update to an existing service.
|
||||
func (gce *GCECloud) UpdateRegionBackendService(bg *compute.BackendService, region string) error {
|
||||
mc := newBackendServiceMetricContext("update", region)
|
||||
op, err := gce.service.RegionBackendServices.Update(gce.projectID, region, bg.Name, bg).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
|
||||
return gce.waitForRegionOp(op, region, mc)
|
||||
}
|
||||
|
||||
// DeleteRegionBackendService deletes the given BackendService by name.
|
||||
func (gce *GCECloud) DeleteRegionBackendService(name, region string) error {
|
||||
mc := newBackendServiceMetricContext("delete", region)
|
||||
op, err := gce.service.RegionBackendServices.Delete(gce.projectID, region, name).Do()
|
||||
if err != nil {
|
||||
if isHTTPErrorCode(err, http.StatusNotFound) {
|
||||
return nil
|
||||
}
|
||||
return mc.Observe(err)
|
||||
}
|
||||
|
||||
return gce.waitForRegionOp(op, region, mc)
|
||||
}
|
||||
|
||||
// CreateRegionBackendService creates the given BackendService.
|
||||
func (gce *GCECloud) CreateRegionBackendService(bg *compute.BackendService, region string) error {
|
||||
mc := newBackendServiceMetricContext("create", region)
|
||||
op, err := gce.service.RegionBackendServices.Insert(gce.projectID, region, bg).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
|
||||
return gce.waitForRegionOp(op, region, mc)
|
||||
}
|
||||
|
||||
// ListRegionBackendServices lists all backend services in the project.
|
||||
func (gce *GCECloud) ListRegionBackendServices(region string) (*compute.BackendServiceList, error) {
|
||||
mc := newBackendServiceMetricContext("list", region)
|
||||
// TODO: use PageToken to list all not just the first 500
|
||||
v, err := gce.service.RegionBackendServices.List(gce.projectID, region).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
||||
|
||||
// GetRegionalBackendServiceHealth returns the health of the BackendService identified by the given
|
||||
// name, in the given instanceGroup. The instanceGroupLink is the fully
|
||||
// qualified self link of an instance group.
|
||||
func (gce *GCECloud) GetRegionalBackendServiceHealth(name, region string, instanceGroupLink string) (*compute.BackendServiceGroupHealth, error) {
|
||||
mc := newBackendServiceMetricContext("get_health", region)
|
||||
groupRef := &compute.ResourceGroupReference{Group: instanceGroupLink}
|
||||
v, err := gce.service.RegionBackendServices.GetHealth(gce.projectID, region, name, groupRef).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
74
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_cert.go
generated
vendored
Normal file
74
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_cert.go
generated
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gce
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
compute "google.golang.org/api/compute/v1"
|
||||
)
|
||||
|
||||
func newCertMetricContext(request string) *metricContext {
|
||||
return newGenericMetricContext("cert", request, unusedMetricLabel, unusedMetricLabel, computeV1Version)
|
||||
}
|
||||
|
||||
// GetSslCertificate returns the SslCertificate by name.
|
||||
func (gce *GCECloud) GetSslCertificate(name string) (*compute.SslCertificate, error) {
|
||||
mc := newCertMetricContext("get")
|
||||
v, err := gce.service.SslCertificates.Get(gce.projectID, name).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
||||
|
||||
// CreateSslCertificate creates and returns a SslCertificate.
|
||||
func (gce *GCECloud) CreateSslCertificate(sslCerts *compute.SslCertificate) (*compute.SslCertificate, error) {
|
||||
mc := newCertMetricContext("create")
|
||||
op, err := gce.service.SslCertificates.Insert(gce.projectID, sslCerts).Do()
|
||||
|
||||
if err != nil {
|
||||
return nil, mc.Observe(err)
|
||||
}
|
||||
|
||||
if err = gce.waitForGlobalOp(op, mc); err != nil {
|
||||
return nil, mc.Observe(err)
|
||||
}
|
||||
|
||||
return gce.GetSslCertificate(sslCerts.Name)
|
||||
}
|
||||
|
||||
// DeleteSslCertificate deletes the SslCertificate by name.
|
||||
func (gce *GCECloud) DeleteSslCertificate(name string) error {
|
||||
mc := newCertMetricContext("delete")
|
||||
op, err := gce.service.SslCertificates.Delete(gce.projectID, name).Do()
|
||||
|
||||
if err != nil {
|
||||
if isHTTPErrorCode(err, http.StatusNotFound) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return mc.Observe(err)
|
||||
}
|
||||
|
||||
return gce.waitForGlobalOp(op, mc)
|
||||
}
|
||||
|
||||
// ListSslCertificates lists all SslCertificates in the project.
|
||||
func (gce *GCECloud) ListSslCertificates() (*compute.SslCertificateList, error) {
|
||||
mc := newCertMetricContext("list")
|
||||
// TODO: use PageToken to list all not just the first 500
|
||||
v, err := gce.service.SslCertificates.List(gce.projectID).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
258
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_clusterid.go
generated
vendored
Normal file
258
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_clusterid.go
generated
vendored
Normal file
@ -0,0 +1,258 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gce
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
const (
|
||||
// Key used to persist UIDs to configmaps.
|
||||
UIDConfigMapName = "ingress-uid"
|
||||
// Namespace which contains the above config map
|
||||
UIDNamespace = metav1.NamespaceSystem
|
||||
// Data keys for the specific ids
|
||||
UIDCluster = "uid"
|
||||
UIDProvider = "provider-uid"
|
||||
UIDLengthBytes = 8
|
||||
// Frequency of the updateFunc event handler being called
|
||||
// This does not actually query the apiserver for current state - the local cache value is used.
|
||||
updateFuncFrequency = 10 * time.Minute
|
||||
)
|
||||
|
||||
type ClusterID struct {
|
||||
idLock sync.RWMutex
|
||||
client clientset.Interface
|
||||
cfgMapKey string
|
||||
store cache.Store
|
||||
providerID *string
|
||||
clusterID *string
|
||||
}
|
||||
|
||||
// Continually watches for changes to the cluster id config map
|
||||
func (gce *GCECloud) watchClusterID() {
|
||||
gce.ClusterID = ClusterID{
|
||||
cfgMapKey: fmt.Sprintf("%v/%v", UIDNamespace, UIDConfigMapName),
|
||||
client: gce.client,
|
||||
}
|
||||
|
||||
mapEventHandler := cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
m, ok := obj.(*v1.ConfigMap)
|
||||
if !ok || m == nil {
|
||||
glog.Errorf("Expected v1.ConfigMap, item=%+v, typeIsOk=%v", obj, ok)
|
||||
return
|
||||
}
|
||||
if m.Namespace != UIDNamespace ||
|
||||
m.Name != UIDConfigMapName {
|
||||
return
|
||||
}
|
||||
|
||||
glog.V(4).Infof("Observed new configmap for clusteriD: %v, %v; setting local values", m.Name, m.Data)
|
||||
gce.ClusterID.update(m)
|
||||
},
|
||||
UpdateFunc: func(old, cur interface{}) {
|
||||
m, ok := cur.(*v1.ConfigMap)
|
||||
if !ok || m == nil {
|
||||
glog.Errorf("Expected v1.ConfigMap, item=%+v, typeIsOk=%v", cur, ok)
|
||||
return
|
||||
}
|
||||
|
||||
if m.Namespace != UIDNamespace ||
|
||||
m.Name != UIDConfigMapName {
|
||||
return
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(old, cur) {
|
||||
return
|
||||
}
|
||||
|
||||
glog.V(4).Infof("Observed updated configmap for clusteriD %v, %v; setting local values", m.Name, m.Data)
|
||||
gce.ClusterID.update(m)
|
||||
},
|
||||
}
|
||||
|
||||
listerWatcher := cache.NewListWatchFromClient(gce.ClusterID.client.CoreV1().RESTClient(), "configmaps", UIDNamespace, fields.Everything())
|
||||
var controller cache.Controller
|
||||
gce.ClusterID.store, controller = cache.NewInformer(newSingleObjectListerWatcher(listerWatcher, UIDConfigMapName), &v1.ConfigMap{}, updateFuncFrequency, mapEventHandler)
|
||||
|
||||
controller.Run(nil)
|
||||
}
|
||||
|
||||
// GetID returns the id which is unique to this cluster
|
||||
// if federated, return the provider id (unique to the cluster)
|
||||
// if not federated, return the cluster id
|
||||
func (ci *ClusterID) GetID() (string, error) {
|
||||
if err := ci.getOrInitialize(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
ci.idLock.RLock()
|
||||
defer ci.idLock.RUnlock()
|
||||
if ci.clusterID == nil {
|
||||
return "", errors.New("Could not retrieve cluster id")
|
||||
}
|
||||
|
||||
// If provider ID is set, (Federation is enabled) use this field
|
||||
if ci.providerID != nil {
|
||||
return *ci.providerID, nil
|
||||
}
|
||||
|
||||
// providerID is not set, use the cluster id
|
||||
return *ci.clusterID, nil
|
||||
}
|
||||
|
||||
// GetFederationId returns the id which could represent the entire Federation
|
||||
// or just the cluster if not federated.
|
||||
func (ci *ClusterID) GetFederationId() (string, bool, error) {
|
||||
if err := ci.getOrInitialize(); err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
|
||||
ci.idLock.RLock()
|
||||
defer ci.idLock.RUnlock()
|
||||
if ci.clusterID == nil {
|
||||
return "", false, errors.New("Could not retrieve cluster id")
|
||||
}
|
||||
|
||||
// If provider ID is not set, return false
|
||||
if ci.providerID == nil || *ci.clusterID == *ci.providerID {
|
||||
return "", false, nil
|
||||
}
|
||||
|
||||
return *ci.clusterID, true, nil
|
||||
}
|
||||
|
||||
// getOrInitialize either grabs the configmaps current value or defines the value
|
||||
// and sets the configmap. This is for the case of the user calling GetClusterID()
|
||||
// before the watch has begun.
|
||||
func (ci *ClusterID) getOrInitialize() error {
|
||||
if ci.store == nil {
|
||||
return errors.New("GCECloud.ClusterID is not ready. Call Initialize() before using.")
|
||||
}
|
||||
|
||||
if ci.clusterID != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
exists, err := ci.getConfigMap()
|
||||
if err != nil {
|
||||
return err
|
||||
} else if exists {
|
||||
return nil
|
||||
}
|
||||
|
||||
// The configmap does not exist - let's try creating one.
|
||||
newId, err := makeUID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
glog.V(4).Infof("Creating clusteriD: %v", newId)
|
||||
cfg := &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: UIDConfigMapName,
|
||||
Namespace: UIDNamespace,
|
||||
},
|
||||
}
|
||||
cfg.Data = map[string]string{
|
||||
UIDCluster: newId,
|
||||
UIDProvider: newId,
|
||||
}
|
||||
|
||||
if _, err := ci.client.CoreV1().ConfigMaps(UIDNamespace).Create(cfg); err != nil {
|
||||
glog.Errorf("GCE cloud provider failed to create %v config map to store cluster id: %v", ci.cfgMapKey, err)
|
||||
return err
|
||||
}
|
||||
|
||||
glog.V(2).Infof("Created a config map containing clusteriD: %v", newId)
|
||||
ci.update(cfg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ci *ClusterID) getConfigMap() (bool, error) {
|
||||
item, exists, err := ci.store.GetByKey(ci.cfgMapKey)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !exists {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
m, ok := item.(*v1.ConfigMap)
|
||||
if !ok || m == nil {
|
||||
err = fmt.Errorf("Expected v1.ConfigMap, item=%+v, typeIsOk=%v", item, ok)
|
||||
glog.Error(err)
|
||||
return false, err
|
||||
}
|
||||
ci.update(m)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (ci *ClusterID) update(m *v1.ConfigMap) {
|
||||
ci.idLock.Lock()
|
||||
defer ci.idLock.Unlock()
|
||||
if clusterID, exists := m.Data[UIDCluster]; exists {
|
||||
ci.clusterID = &clusterID
|
||||
}
|
||||
if provId, exists := m.Data[UIDProvider]; exists {
|
||||
ci.providerID = &provId
|
||||
}
|
||||
}
|
||||
|
||||
func makeUID() (string, error) {
|
||||
b := make([]byte, UIDLengthBytes)
|
||||
_, err := rand.Read(b)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return hex.EncodeToString(b), nil
|
||||
}
|
||||
|
||||
func newSingleObjectListerWatcher(lw cache.ListerWatcher, objectName string) *singleObjListerWatcher {
|
||||
return &singleObjListerWatcher{lw: lw, objectName: objectName}
|
||||
}
|
||||
|
||||
type singleObjListerWatcher struct {
|
||||
lw cache.ListerWatcher
|
||||
objectName string
|
||||
}
|
||||
|
||||
func (sow *singleObjListerWatcher) List(options metav1.ListOptions) (runtime.Object, error) {
|
||||
options.FieldSelector = "metadata.name=" + sow.objectName
|
||||
return sow.lw.List(options)
|
||||
}
|
||||
|
||||
func (sow *singleObjListerWatcher) Watch(options metav1.ListOptions) (watch.Interface, error) {
|
||||
options.FieldSelector = "metadata.name=" + sow.objectName
|
||||
return sow.lw.Watch(options)
|
||||
}
|
55
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_clusters.go
generated
vendored
Normal file
55
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_clusters.go
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gce
|
||||
|
||||
func newClustersMetricContext(request, zone string) *metricContext {
|
||||
return newGenericMetricContext("clusters", request, unusedMetricLabel, zone, computeV1Version)
|
||||
}
|
||||
|
||||
func (gce *GCECloud) ListClusters() ([]string, error) {
|
||||
allClusters := []string{}
|
||||
|
||||
for _, zone := range gce.managedZones {
|
||||
clusters, err := gce.listClustersInZone(zone)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// TODO: Scoping? Do we need to qualify the cluster name?
|
||||
allClusters = append(allClusters, clusters...)
|
||||
}
|
||||
|
||||
return allClusters, nil
|
||||
}
|
||||
|
||||
func (gce *GCECloud) Master(clusterName string) (string, error) {
|
||||
return "k8s-" + clusterName + "-master.internal", nil
|
||||
}
|
||||
|
||||
func (gce *GCECloud) listClustersInZone(zone string) ([]string, error) {
|
||||
mc := newClustersMetricContext("list_zone", zone)
|
||||
// TODO: use PageToken to list all not just the first 500
|
||||
list, err := gce.containerService.Projects.Zones.Clusters.List(gce.projectID, zone).Do()
|
||||
if err != nil {
|
||||
return nil, mc.Observe(err)
|
||||
}
|
||||
|
||||
result := []string{}
|
||||
for _, cluster := range list.Clusters {
|
||||
result = append(result, cluster.Name)
|
||||
}
|
||||
return result, mc.Observe(nil)
|
||||
}
|
1124
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_disks.go
generated
vendored
Normal file
1124
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_disks.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1009
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_disks_test.go
generated
vendored
Normal file
1009
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_disks_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
64
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_firewall.go
generated
vendored
Normal file
64
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_firewall.go
generated
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gce
|
||||
|
||||
import (
|
||||
compute "google.golang.org/api/compute/v1"
|
||||
)
|
||||
|
||||
func newFirewallMetricContext(request string) *metricContext {
|
||||
return newGenericMetricContext("firewall", request, unusedMetricLabel, unusedMetricLabel, computeV1Version)
|
||||
}
|
||||
|
||||
// GetFirewall returns the Firewall by name.
|
||||
func (gce *GCECloud) GetFirewall(name string) (*compute.Firewall, error) {
|
||||
mc := newFirewallMetricContext("get")
|
||||
v, err := gce.service.Firewalls.Get(gce.NetworkProjectID(), name).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
||||
|
||||
// CreateFirewall creates the passed firewall
|
||||
func (gce *GCECloud) CreateFirewall(f *compute.Firewall) error {
|
||||
mc := newFirewallMetricContext("create")
|
||||
op, err := gce.service.Firewalls.Insert(gce.NetworkProjectID(), f).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
|
||||
return gce.waitForGlobalOpInProject(op, gce.NetworkProjectID(), mc)
|
||||
}
|
||||
|
||||
// DeleteFirewall deletes the given firewall rule.
|
||||
func (gce *GCECloud) DeleteFirewall(name string) error {
|
||||
mc := newFirewallMetricContext("delete")
|
||||
op, err := gce.service.Firewalls.Delete(gce.NetworkProjectID(), name).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
return gce.waitForGlobalOpInProject(op, gce.NetworkProjectID(), mc)
|
||||
}
|
||||
|
||||
// UpdateFirewall applies the given firewall as an update to an existing service.
|
||||
func (gce *GCECloud) UpdateFirewall(f *compute.Firewall) error {
|
||||
mc := newFirewallMetricContext("update")
|
||||
op, err := gce.service.Firewalls.Update(gce.NetworkProjectID(), f.Name, f).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
|
||||
return gce.waitForGlobalOpInProject(op, gce.NetworkProjectID(), mc)
|
||||
}
|
155
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_forwardingrule.go
generated
vendored
Normal file
155
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_forwardingrule.go
generated
vendored
Normal file
@ -0,0 +1,155 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gce
|
||||
|
||||
import (
|
||||
computealpha "google.golang.org/api/compute/v0.alpha"
|
||||
compute "google.golang.org/api/compute/v1"
|
||||
)
|
||||
|
||||
func newForwardingRuleMetricContext(request, region string) *metricContext {
|
||||
return newForwardingRuleMetricContextWithVersion(request, region, computeV1Version)
|
||||
}
|
||||
func newForwardingRuleMetricContextWithVersion(request, region, version string) *metricContext {
|
||||
return newGenericMetricContext("forwardingrule", request, region, unusedMetricLabel, version)
|
||||
}
|
||||
|
||||
// CreateGlobalForwardingRule creates the passed GlobalForwardingRule
|
||||
func (gce *GCECloud) CreateGlobalForwardingRule(rule *compute.ForwardingRule) error {
|
||||
mc := newForwardingRuleMetricContext("create", "")
|
||||
op, err := gce.service.GlobalForwardingRules.Insert(gce.projectID, rule).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
return gce.waitForGlobalOp(op, mc)
|
||||
}
|
||||
|
||||
// SetProxyForGlobalForwardingRule links the given TargetHttp(s)Proxy with the given GlobalForwardingRule.
|
||||
// targetProxyLink is the SelfLink of a TargetHttp(s)Proxy.
|
||||
func (gce *GCECloud) SetProxyForGlobalForwardingRule(forwardingRuleName, targetProxyLink string) error {
|
||||
mc := newForwardingRuleMetricContext("set_proxy", "")
|
||||
op, err := gce.service.GlobalForwardingRules.SetTarget(
|
||||
gce.projectID, forwardingRuleName, &compute.TargetReference{Target: targetProxyLink}).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
|
||||
return gce.waitForGlobalOp(op, mc)
|
||||
}
|
||||
|
||||
// DeleteGlobalForwardingRule deletes the GlobalForwardingRule by name.
|
||||
func (gce *GCECloud) DeleteGlobalForwardingRule(name string) error {
|
||||
mc := newForwardingRuleMetricContext("delete", "")
|
||||
op, err := gce.service.GlobalForwardingRules.Delete(gce.projectID, name).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
|
||||
return gce.waitForGlobalOp(op, mc)
|
||||
}
|
||||
|
||||
// GetGlobalForwardingRule returns the GlobalForwardingRule by name.
|
||||
func (gce *GCECloud) GetGlobalForwardingRule(name string) (*compute.ForwardingRule, error) {
|
||||
mc := newForwardingRuleMetricContext("get", "")
|
||||
v, err := gce.service.GlobalForwardingRules.Get(gce.projectID, name).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
||||
|
||||
// ListGlobalForwardingRules lists all GlobalForwardingRules in the project.
|
||||
func (gce *GCECloud) ListGlobalForwardingRules() (*compute.ForwardingRuleList, error) {
|
||||
mc := newForwardingRuleMetricContext("list", "")
|
||||
// TODO: use PageToken to list all not just the first 500
|
||||
v, err := gce.service.GlobalForwardingRules.List(gce.projectID).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
||||
|
||||
// GetRegionForwardingRule returns the RegionalForwardingRule by name & region.
|
||||
func (gce *GCECloud) GetRegionForwardingRule(name, region string) (*compute.ForwardingRule, error) {
|
||||
mc := newForwardingRuleMetricContext("get", region)
|
||||
v, err := gce.service.ForwardingRules.Get(gce.projectID, region, name).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
||||
|
||||
// GetAlphaRegionForwardingRule returns the Alpha forwarding rule by name & region.
|
||||
func (gce *GCECloud) GetAlphaRegionForwardingRule(name, region string) (*computealpha.ForwardingRule, error) {
|
||||
mc := newForwardingRuleMetricContextWithVersion("get", region, computeAlphaVersion)
|
||||
v, err := gce.serviceAlpha.ForwardingRules.Get(gce.projectID, region, name).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
||||
|
||||
// ListRegionForwardingRules lists all RegionalForwardingRules in the project & region.
|
||||
func (gce *GCECloud) ListRegionForwardingRules(region string) (*compute.ForwardingRuleList, error) {
|
||||
mc := newForwardingRuleMetricContext("list", region)
|
||||
// TODO: use PageToken to list all not just the first 500
|
||||
v, err := gce.service.ForwardingRules.List(gce.projectID, region).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
||||
|
||||
// ListRegionForwardingRules lists all RegionalForwardingRules in the project & region.
|
||||
func (gce *GCECloud) ListAlphaRegionForwardingRules(region string) (*computealpha.ForwardingRuleList, error) {
|
||||
mc := newForwardingRuleMetricContextWithVersion("list", region, computeAlphaVersion)
|
||||
// TODO: use PageToken to list all not just the first 500
|
||||
v, err := gce.serviceAlpha.ForwardingRules.List(gce.projectID, region).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
||||
|
||||
// CreateRegionForwardingRule creates and returns a
|
||||
// RegionalForwardingRule that points to the given BackendService
|
||||
func (gce *GCECloud) CreateRegionForwardingRule(rule *compute.ForwardingRule, region string) error {
|
||||
mc := newForwardingRuleMetricContext("create", region)
|
||||
op, err := gce.service.ForwardingRules.Insert(gce.projectID, region, rule).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
|
||||
return gce.waitForRegionOp(op, region, mc)
|
||||
}
|
||||
|
||||
// CreateAlphaRegionForwardingRule creates and returns an Alpha
|
||||
// forwarding fule in the given region.
|
||||
func (gce *GCECloud) CreateAlphaRegionForwardingRule(rule *computealpha.ForwardingRule, region string) error {
|
||||
mc := newForwardingRuleMetricContextWithVersion("create", region, computeAlphaVersion)
|
||||
op, err := gce.serviceAlpha.ForwardingRules.Insert(gce.projectID, region, rule).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
|
||||
return gce.waitForRegionOp(op, region, mc)
|
||||
}
|
||||
|
||||
// DeleteRegionForwardingRule deletes the RegionalForwardingRule by name & region.
|
||||
func (gce *GCECloud) DeleteRegionForwardingRule(name, region string) error {
|
||||
mc := newForwardingRuleMetricContext("delete", region)
|
||||
op, err := gce.service.ForwardingRules.Delete(gce.projectID, region, name).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
|
||||
return gce.waitForRegionOp(op, region, mc)
|
||||
}
|
||||
|
||||
// TODO(#51665): retire this function once Network Tiers becomes Beta in GCP.
|
||||
func (gce *GCECloud) getNetworkTierFromForwardingRule(name, region string) (string, error) {
|
||||
if !gce.AlphaFeatureGate.Enabled(AlphaFeatureNetworkTiers) {
|
||||
return NetworkTierDefault.ToGCEValue(), nil
|
||||
}
|
||||
fwdRule, err := gce.GetAlphaRegionForwardingRule(name, region)
|
||||
if err != nil {
|
||||
return handleAlphaNetworkTierGetError(err)
|
||||
}
|
||||
return fwdRule.NetworkTier, nil
|
||||
}
|
138
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_forwardingrule_fakes.go
generated
vendored
Normal file
138
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_forwardingrule_fakes.go
generated
vendored
Normal file
@ -0,0 +1,138 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gce
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
computealpha "google.golang.org/api/compute/v0.alpha"
|
||||
compute "google.golang.org/api/compute/v1"
|
||||
"google.golang.org/api/googleapi"
|
||||
)
|
||||
|
||||
type FakeCloudForwardingRuleService struct {
|
||||
// fwdRulesByRegionAndName
|
||||
// Outer key is for region string; inner key is for fwdRuleess name.
|
||||
fwdRulesByRegionAndName map[string]map[string]*computealpha.ForwardingRule
|
||||
}
|
||||
|
||||
// FakeCloudForwardingRuleService Implements CloudForwardingRuleService
|
||||
var _ CloudForwardingRuleService = &FakeCloudForwardingRuleService{}
|
||||
|
||||
func NewFakeCloudForwardingRuleService() *FakeCloudForwardingRuleService {
|
||||
return &FakeCloudForwardingRuleService{
|
||||
fwdRulesByRegionAndName: make(map[string]map[string]*computealpha.ForwardingRule),
|
||||
}
|
||||
}
|
||||
|
||||
// SetRegionalForwardingRulees sets the fwdRuleesses of ther region. This is used for
|
||||
// setting the test environment.
|
||||
func (f *FakeCloudForwardingRuleService) SetRegionalForwardingRulees(region string, fwdRules []*computealpha.ForwardingRule) {
|
||||
// Reset fwdRuleesses in the region.
|
||||
f.fwdRulesByRegionAndName[region] = make(map[string]*computealpha.ForwardingRule)
|
||||
|
||||
for _, fwdRule := range fwdRules {
|
||||
f.fwdRulesByRegionAndName[region][fwdRule.Name] = fwdRule
|
||||
}
|
||||
}
|
||||
|
||||
func (f *FakeCloudForwardingRuleService) CreateAlphaRegionForwardingRule(fwdRule *computealpha.ForwardingRule, region string) error {
|
||||
if _, exists := f.fwdRulesByRegionAndName[region]; !exists {
|
||||
f.fwdRulesByRegionAndName[region] = make(map[string]*computealpha.ForwardingRule)
|
||||
}
|
||||
|
||||
if _, exists := f.fwdRulesByRegionAndName[region][fwdRule.Name]; exists {
|
||||
return &googleapi.Error{Code: http.StatusConflict}
|
||||
}
|
||||
|
||||
f.fwdRulesByRegionAndName[region][fwdRule.Name] = fwdRule
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FakeCloudForwardingRuleService) CreateRegionForwardingRule(fwdRule *compute.ForwardingRule, region string) error {
|
||||
alphafwdRule := convertToAlphaForwardingRule(fwdRule)
|
||||
return f.CreateAlphaRegionForwardingRule(alphafwdRule, region)
|
||||
}
|
||||
|
||||
func (f *FakeCloudForwardingRuleService) DeleteRegionForwardingRule(name, region string) error {
|
||||
if _, exists := f.fwdRulesByRegionAndName[region]; !exists {
|
||||
return makeGoogleAPINotFoundError("")
|
||||
}
|
||||
|
||||
if _, exists := f.fwdRulesByRegionAndName[region][name]; !exists {
|
||||
return makeGoogleAPINotFoundError("")
|
||||
}
|
||||
delete(f.fwdRulesByRegionAndName[region], name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FakeCloudForwardingRuleService) GetAlphaRegionForwardingRule(name, region string) (*computealpha.ForwardingRule, error) {
|
||||
if _, exists := f.fwdRulesByRegionAndName[region]; !exists {
|
||||
return nil, makeGoogleAPINotFoundError("")
|
||||
}
|
||||
|
||||
if fwdRule, exists := f.fwdRulesByRegionAndName[region][name]; !exists {
|
||||
return nil, makeGoogleAPINotFoundError("")
|
||||
} else {
|
||||
return fwdRule, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (f *FakeCloudForwardingRuleService) GetRegionForwardingRule(name, region string) (*compute.ForwardingRule, error) {
|
||||
fwdRule, err := f.GetAlphaRegionForwardingRule(name, region)
|
||||
if fwdRule != nil {
|
||||
return convertToV1ForwardingRule(fwdRule), err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (f *FakeCloudForwardingRuleService) getNetworkTierFromForwardingRule(name, region string) (string, error) {
|
||||
fwdRule, err := f.GetAlphaRegionForwardingRule(name, region)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fwdRule.NetworkTier, nil
|
||||
}
|
||||
|
||||
func convertToV1ForwardingRule(object gceObject) *compute.ForwardingRule {
|
||||
enc, err := object.MarshalJSON()
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Failed to encode to json: %v", err))
|
||||
}
|
||||
var fwdRule compute.ForwardingRule
|
||||
if err := json.Unmarshal(enc, &fwdRule); err != nil {
|
||||
panic(fmt.Sprintf("Failed to convert GCE apiObject %v to v1 fwdRuleess: %v", object, err))
|
||||
}
|
||||
return &fwdRule
|
||||
}
|
||||
|
||||
func convertToAlphaForwardingRule(object gceObject) *computealpha.ForwardingRule {
|
||||
enc, err := object.MarshalJSON()
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Failed to encode to json: %v", err))
|
||||
}
|
||||
var fwdRule computealpha.ForwardingRule
|
||||
if err := json.Unmarshal(enc, &fwdRule); err != nil {
|
||||
panic(fmt.Sprintf("Failed to convert GCE apiObject %v to alpha fwdRuleess: %v", object, err))
|
||||
}
|
||||
// Set the default values for the Alpha fields.
|
||||
fwdRule.NetworkTier = NetworkTierDefault.ToGCEValue()
|
||||
|
||||
return &fwdRule
|
||||
}
|
265
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_healthchecks.go
generated
vendored
Normal file
265
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_healthchecks.go
generated
vendored
Normal file
@ -0,0 +1,265 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gce
|
||||
|
||||
import (
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/kubernetes/pkg/master/ports"
|
||||
utilversion "k8s.io/kubernetes/pkg/util/version"
|
||||
|
||||
"github.com/golang/glog"
|
||||
computealpha "google.golang.org/api/compute/v0.alpha"
|
||||
compute "google.golang.org/api/compute/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
nodesHealthCheckPath = "/healthz"
|
||||
lbNodesHealthCheckPort = ports.ProxyHealthzPort
|
||||
)
|
||||
|
||||
var (
|
||||
minNodesHealthCheckVersion *utilversion.Version
|
||||
)
|
||||
|
||||
func init() {
|
||||
if v, err := utilversion.ParseGeneric("1.7.2"); err != nil {
|
||||
glog.Fatalf("Failed to parse version for minNodesHealthCheckVersion: %v", err)
|
||||
} else {
|
||||
minNodesHealthCheckVersion = v
|
||||
}
|
||||
}
|
||||
|
||||
func newHealthcheckMetricContext(request string) *metricContext {
|
||||
return newHealthcheckMetricContextWithVersion(request, computeV1Version)
|
||||
}
|
||||
|
||||
func newHealthcheckMetricContextWithVersion(request, version string) *metricContext {
|
||||
return newGenericMetricContext("healthcheck", request, unusedMetricLabel, unusedMetricLabel, version)
|
||||
}
|
||||
|
||||
// GetHttpHealthCheck returns the given HttpHealthCheck by name.
|
||||
func (gce *GCECloud) GetHttpHealthCheck(name string) (*compute.HttpHealthCheck, error) {
|
||||
mc := newHealthcheckMetricContext("get_legacy")
|
||||
v, err := gce.service.HttpHealthChecks.Get(gce.projectID, name).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
||||
|
||||
// UpdateHttpHealthCheck applies the given HttpHealthCheck as an update.
|
||||
func (gce *GCECloud) UpdateHttpHealthCheck(hc *compute.HttpHealthCheck) error {
|
||||
mc := newHealthcheckMetricContext("update_legacy")
|
||||
op, err := gce.service.HttpHealthChecks.Update(gce.projectID, hc.Name, hc).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
|
||||
return gce.waitForGlobalOp(op, mc)
|
||||
}
|
||||
|
||||
// DeleteHttpHealthCheck deletes the given HttpHealthCheck by name.
|
||||
func (gce *GCECloud) DeleteHttpHealthCheck(name string) error {
|
||||
mc := newHealthcheckMetricContext("delete_legacy")
|
||||
op, err := gce.service.HttpHealthChecks.Delete(gce.projectID, name).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
|
||||
return gce.waitForGlobalOp(op, mc)
|
||||
}
|
||||
|
||||
// CreateHttpHealthCheck creates the given HttpHealthCheck.
|
||||
func (gce *GCECloud) CreateHttpHealthCheck(hc *compute.HttpHealthCheck) error {
|
||||
mc := newHealthcheckMetricContext("create_legacy")
|
||||
op, err := gce.service.HttpHealthChecks.Insert(gce.projectID, hc).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
|
||||
return gce.waitForGlobalOp(op, mc)
|
||||
}
|
||||
|
||||
// ListHttpHealthChecks lists all HttpHealthChecks in the project.
|
||||
func (gce *GCECloud) ListHttpHealthChecks() (*compute.HttpHealthCheckList, error) {
|
||||
mc := newHealthcheckMetricContext("list_legacy")
|
||||
// TODO: use PageToken to list all not just the first 500
|
||||
v, err := gce.service.HttpHealthChecks.List(gce.projectID).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
||||
|
||||
// Legacy HTTPS Health Checks
|
||||
|
||||
// GetHttpsHealthCheck returns the given HttpsHealthCheck by name.
|
||||
func (gce *GCECloud) GetHttpsHealthCheck(name string) (*compute.HttpsHealthCheck, error) {
|
||||
mc := newHealthcheckMetricContext("get_legacy")
|
||||
v, err := gce.service.HttpsHealthChecks.Get(gce.projectID, name).Do()
|
||||
mc.Observe(err)
|
||||
return v, err
|
||||
}
|
||||
|
||||
// UpdateHttpsHealthCheck applies the given HttpsHealthCheck as an update.
|
||||
func (gce *GCECloud) UpdateHttpsHealthCheck(hc *compute.HttpsHealthCheck) error {
|
||||
mc := newHealthcheckMetricContext("update_legacy")
|
||||
op, err := gce.service.HttpsHealthChecks.Update(gce.projectID, hc.Name, hc).Do()
|
||||
if err != nil {
|
||||
mc.Observe(err)
|
||||
return err
|
||||
}
|
||||
|
||||
return gce.waitForGlobalOp(op, mc)
|
||||
}
|
||||
|
||||
// DeleteHttpsHealthCheck deletes the given HttpsHealthCheck by name.
|
||||
func (gce *GCECloud) DeleteHttpsHealthCheck(name string) error {
|
||||
mc := newHealthcheckMetricContext("delete_legacy")
|
||||
op, err := gce.service.HttpsHealthChecks.Delete(gce.projectID, name).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
|
||||
return gce.waitForGlobalOp(op, mc)
|
||||
}
|
||||
|
||||
// CreateHttpsHealthCheck creates the given HttpsHealthCheck.
|
||||
func (gce *GCECloud) CreateHttpsHealthCheck(hc *compute.HttpsHealthCheck) error {
|
||||
mc := newHealthcheckMetricContext("create_legacy")
|
||||
op, err := gce.service.HttpsHealthChecks.Insert(gce.projectID, hc).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
|
||||
return gce.waitForGlobalOp(op, mc)
|
||||
}
|
||||
|
||||
// ListHttpsHealthChecks lists all HttpsHealthChecks in the project.
|
||||
func (gce *GCECloud) ListHttpsHealthChecks() (*compute.HttpsHealthCheckList, error) {
|
||||
mc := newHealthcheckMetricContext("list_legacy")
|
||||
// TODO: use PageToken to list all not just the first 500
|
||||
v, err := gce.service.HttpsHealthChecks.List(gce.projectID).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
||||
|
||||
// Generic HealthCheck
|
||||
|
||||
// GetHealthCheck returns the given HealthCheck by name.
|
||||
func (gce *GCECloud) GetHealthCheck(name string) (*compute.HealthCheck, error) {
|
||||
mc := newHealthcheckMetricContext("get")
|
||||
v, err := gce.service.HealthChecks.Get(gce.projectID, name).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
||||
|
||||
// GetAlphaHealthCheck returns the given alpha HealthCheck by name.
|
||||
func (gce *GCECloud) GetAlphaHealthCheck(name string) (*computealpha.HealthCheck, error) {
|
||||
mc := newHealthcheckMetricContextWithVersion("get", computeAlphaVersion)
|
||||
v, err := gce.serviceAlpha.HealthChecks.Get(gce.projectID, name).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
||||
|
||||
// UpdateHealthCheck applies the given HealthCheck as an update.
|
||||
func (gce *GCECloud) UpdateHealthCheck(hc *compute.HealthCheck) error {
|
||||
mc := newHealthcheckMetricContext("update")
|
||||
op, err := gce.service.HealthChecks.Update(gce.projectID, hc.Name, hc).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
|
||||
return gce.waitForGlobalOp(op, mc)
|
||||
}
|
||||
|
||||
// UpdateAlphaHealthCheck applies the given alpha HealthCheck as an update.
|
||||
func (gce *GCECloud) UpdateAlphaHealthCheck(hc *computealpha.HealthCheck) error {
|
||||
mc := newHealthcheckMetricContextWithVersion("update", computeAlphaVersion)
|
||||
op, err := gce.serviceAlpha.HealthChecks.Update(gce.projectID, hc.Name, hc).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
|
||||
return gce.waitForGlobalOp(op, mc)
|
||||
}
|
||||
|
||||
// DeleteHealthCheck deletes the given HealthCheck by name.
|
||||
func (gce *GCECloud) DeleteHealthCheck(name string) error {
|
||||
mc := newHealthcheckMetricContext("delete")
|
||||
op, err := gce.service.HealthChecks.Delete(gce.projectID, name).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
|
||||
return gce.waitForGlobalOp(op, mc)
|
||||
}
|
||||
|
||||
// CreateHealthCheck creates the given HealthCheck.
|
||||
func (gce *GCECloud) CreateHealthCheck(hc *compute.HealthCheck) error {
|
||||
mc := newHealthcheckMetricContext("create")
|
||||
op, err := gce.service.HealthChecks.Insert(gce.projectID, hc).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
|
||||
return gce.waitForGlobalOp(op, mc)
|
||||
}
|
||||
|
||||
// CreateAlphaHealthCheck creates the given alpha HealthCheck.
|
||||
func (gce *GCECloud) CreateAlphaHealthCheck(hc *computealpha.HealthCheck) error {
|
||||
mc := newHealthcheckMetricContextWithVersion("create", computeAlphaVersion)
|
||||
op, err := gce.serviceAlpha.HealthChecks.Insert(gce.projectID, hc).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
|
||||
return gce.waitForGlobalOp(op, mc)
|
||||
}
|
||||
|
||||
// ListHealthChecks lists all HealthCheck in the project.
|
||||
func (gce *GCECloud) ListHealthChecks() (*compute.HealthCheckList, error) {
|
||||
mc := newHealthcheckMetricContext("list")
|
||||
// TODO: use PageToken to list all not just the first 500
|
||||
v, err := gce.service.HealthChecks.List(gce.projectID).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
||||
|
||||
// GetNodesHealthCheckPort returns the health check port used by the GCE load
|
||||
// balancers (l4) for performing health checks on nodes.
|
||||
func GetNodesHealthCheckPort() int32 {
|
||||
return lbNodesHealthCheckPort
|
||||
}
|
||||
|
||||
// GetNodesHealthCheckPath returns the health check path used by the GCE load
|
||||
// balancers (l4) for performing health checks on nodes.
|
||||
func GetNodesHealthCheckPath() string {
|
||||
return nodesHealthCheckPath
|
||||
}
|
||||
|
||||
// isAtLeastMinNodesHealthCheckVersion checks if a version is higher than
|
||||
// `minNodesHealthCheckVersion`.
|
||||
func isAtLeastMinNodesHealthCheckVersion(vstring string) bool {
|
||||
version, err := utilversion.ParseGeneric(vstring)
|
||||
if err != nil {
|
||||
glog.Errorf("vstring (%s) is not a valid version string: %v", vstring, err)
|
||||
return false
|
||||
}
|
||||
return version.AtLeast(minNodesHealthCheckVersion)
|
||||
}
|
||||
|
||||
// supportsNodesHealthCheck returns false if anyone of the nodes has version
|
||||
// lower than `minNodesHealthCheckVersion`.
|
||||
func supportsNodesHealthCheck(nodes []*v1.Node) bool {
|
||||
for _, node := range nodes {
|
||||
if !isAtLeastMinNodesHealthCheckVersion(node.Status.NodeInfo.KubeProxyVersion) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
124
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_healthchecks_test.go
generated
vendored
Normal file
124
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_healthchecks_test.go
generated
vendored
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gce
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func TestIsAtLeastMinNodesHealthCheckVersion(t *testing.T) {
|
||||
testCases := []struct {
|
||||
version string
|
||||
expect bool
|
||||
}{
|
||||
{"v1.7.3", true},
|
||||
{"v1.7.2", true},
|
||||
{"v1.7.2-alpha.2.597+276d289b90d322", true},
|
||||
{"v1.6.0-beta.3.472+831q821c907t31a", false},
|
||||
{"v1.5.2", false},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
if res := isAtLeastMinNodesHealthCheckVersion(tc.version); res != tc.expect {
|
||||
t.Errorf("%v: want %v, got %v", tc.version, tc.expect, res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSupportsNodesHealthCheck(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
nodes []*v1.Node
|
||||
expect bool
|
||||
}{
|
||||
{
|
||||
"All nodes support nodes health check",
|
||||
[]*v1.Node{
|
||||
{
|
||||
Status: v1.NodeStatus{
|
||||
NodeInfo: v1.NodeSystemInfo{
|
||||
KubeProxyVersion: "v1.7.2",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Status: v1.NodeStatus{
|
||||
NodeInfo: v1.NodeSystemInfo{
|
||||
KubeProxyVersion: "v1.7.2-alpha.2.597+276d289b90d322",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"All nodes don't support nodes health check",
|
||||
[]*v1.Node{
|
||||
{
|
||||
Status: v1.NodeStatus{
|
||||
NodeInfo: v1.NodeSystemInfo{
|
||||
KubeProxyVersion: "v1.6.0-beta.3.472+831q821c907t31a",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Status: v1.NodeStatus{
|
||||
NodeInfo: v1.NodeSystemInfo{
|
||||
KubeProxyVersion: "v1.5.2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"One node doesn't support nodes health check",
|
||||
[]*v1.Node{
|
||||
{
|
||||
Status: v1.NodeStatus{
|
||||
NodeInfo: v1.NodeSystemInfo{
|
||||
KubeProxyVersion: "v1.7.3",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Status: v1.NodeStatus{
|
||||
NodeInfo: v1.NodeSystemInfo{
|
||||
KubeProxyVersion: "v1.7.2-alpha.2.597+276d289b90d322",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Status: v1.NodeStatus{
|
||||
NodeInfo: v1.NodeSystemInfo{
|
||||
KubeProxyVersion: "v1.5.2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
if res := supportsNodesHealthCheck(tc.nodes); res != tc.expect {
|
||||
t.Errorf("%v: want %v, got %v", tc.desc, tc.expect, res)
|
||||
}
|
||||
}
|
||||
}
|
127
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_instancegroup.go
generated
vendored
Normal file
127
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_instancegroup.go
generated
vendored
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gce
|
||||
|
||||
import compute "google.golang.org/api/compute/v1"
|
||||
|
||||
func newInstanceGroupMetricContext(request string, zone string) *metricContext {
|
||||
return newGenericMetricContext("instancegroup", request, unusedMetricLabel, zone, computeV1Version)
|
||||
}
|
||||
|
||||
// CreateInstanceGroup creates an instance group with the given
|
||||
// instances. It is the callers responsibility to add named ports.
|
||||
func (gce *GCECloud) CreateInstanceGroup(ig *compute.InstanceGroup, zone string) error {
|
||||
mc := newInstanceGroupMetricContext("create", zone)
|
||||
op, err := gce.service.InstanceGroups.Insert(gce.projectID, zone, ig).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
|
||||
return gce.waitForZoneOp(op, zone, mc)
|
||||
}
|
||||
|
||||
// DeleteInstanceGroup deletes an instance group.
|
||||
func (gce *GCECloud) DeleteInstanceGroup(name string, zone string) error {
|
||||
mc := newInstanceGroupMetricContext("delete", zone)
|
||||
op, err := gce.service.InstanceGroups.Delete(
|
||||
gce.projectID, zone, name).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
|
||||
return gce.waitForZoneOp(op, zone, mc)
|
||||
}
|
||||
|
||||
// ListInstanceGroups lists all InstanceGroups in the project and
|
||||
// zone.
|
||||
func (gce *GCECloud) ListInstanceGroups(zone string) (*compute.InstanceGroupList, error) {
|
||||
mc := newInstanceGroupMetricContext("list", zone)
|
||||
// TODO: use PageToken to list all not just the first 500
|
||||
v, err := gce.service.InstanceGroups.List(gce.projectID, zone).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
||||
|
||||
// ListInstancesInInstanceGroup lists all the instances in a given
|
||||
// instance group and state.
|
||||
func (gce *GCECloud) ListInstancesInInstanceGroup(name string, zone string, state string) (*compute.InstanceGroupsListInstances, error) {
|
||||
mc := newInstanceGroupMetricContext("list_instances", zone)
|
||||
// TODO: use PageToken to list all not just the first 500
|
||||
v, err := gce.service.InstanceGroups.ListInstances(
|
||||
gce.projectID, zone, name,
|
||||
&compute.InstanceGroupsListInstancesRequest{InstanceState: state}).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
||||
|
||||
// AddInstancesToInstanceGroup adds the given instances to the given
|
||||
// instance group.
|
||||
func (gce *GCECloud) AddInstancesToInstanceGroup(name string, zone string, instanceRefs []*compute.InstanceReference) error {
|
||||
mc := newInstanceGroupMetricContext("add_instances", zone)
|
||||
if len(instanceRefs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
op, err := gce.service.InstanceGroups.AddInstances(
|
||||
gce.projectID, zone, name,
|
||||
&compute.InstanceGroupsAddInstancesRequest{
|
||||
Instances: instanceRefs,
|
||||
}).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
|
||||
return gce.waitForZoneOp(op, zone, mc)
|
||||
}
|
||||
|
||||
// RemoveInstancesFromInstanceGroup removes the given instances from
|
||||
// the instance group.
|
||||
func (gce *GCECloud) RemoveInstancesFromInstanceGroup(name string, zone string, instanceRefs []*compute.InstanceReference) error {
|
||||
mc := newInstanceGroupMetricContext("remove_instances", zone)
|
||||
if len(instanceRefs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
op, err := gce.service.InstanceGroups.RemoveInstances(
|
||||
gce.projectID, zone, name,
|
||||
&compute.InstanceGroupsRemoveInstancesRequest{
|
||||
Instances: instanceRefs,
|
||||
}).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
|
||||
return gce.waitForZoneOp(op, zone, mc)
|
||||
}
|
||||
|
||||
// SetNamedPortsOfInstanceGroup sets the list of named ports on a given instance group
|
||||
func (gce *GCECloud) SetNamedPortsOfInstanceGroup(igName, zone string, namedPorts []*compute.NamedPort) error {
|
||||
mc := newInstanceGroupMetricContext("set_namedports", zone)
|
||||
op, err := gce.service.InstanceGroups.SetNamedPorts(
|
||||
gce.projectID, zone, igName,
|
||||
&compute.InstanceGroupsSetNamedPortsRequest{NamedPorts: namedPorts}).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
|
||||
return gce.waitForZoneOp(op, zone, mc)
|
||||
}
|
||||
|
||||
// GetInstanceGroup returns an instance group by name.
|
||||
func (gce *GCECloud) GetInstanceGroup(name string, zone string) (*compute.InstanceGroup, error) {
|
||||
mc := newInstanceGroupMetricContext("get", zone)
|
||||
v, err := gce.service.InstanceGroups.Get(gce.projectID, zone, name).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
690
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_instances.go
generated
vendored
Normal file
690
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_instances.go
generated
vendored
Normal file
@ -0,0 +1,690 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gce
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"cloud.google.com/go/compute/metadata"
|
||||
"github.com/golang/glog"
|
||||
computealpha "google.golang.org/api/compute/v0.alpha"
|
||||
computebeta "google.golang.org/api/compute/v0.beta"
|
||||
compute "google.golang.org/api/compute/v1"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/kubernetes/pkg/cloudprovider"
|
||||
kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultZone = ""
|
||||
)
|
||||
|
||||
func newInstancesMetricContext(request, zone string) *metricContext {
|
||||
return newGenericMetricContext("instances", request, unusedMetricLabel, zone, computeV1Version)
|
||||
}
|
||||
|
||||
func splitNodesByZone(nodes []*v1.Node) map[string][]*v1.Node {
|
||||
zones := make(map[string][]*v1.Node)
|
||||
for _, n := range nodes {
|
||||
z := getZone(n)
|
||||
if z != defaultZone {
|
||||
zones[z] = append(zones[z], n)
|
||||
}
|
||||
}
|
||||
return zones
|
||||
}
|
||||
|
||||
func getZone(n *v1.Node) string {
|
||||
zone, ok := n.Labels[kubeletapis.LabelZoneFailureDomain]
|
||||
if !ok {
|
||||
return defaultZone
|
||||
}
|
||||
return zone
|
||||
}
|
||||
|
||||
// ToInstanceReferences returns instance references by links
|
||||
func (gce *GCECloud) ToInstanceReferences(zone string, instanceNames []string) (refs []*compute.InstanceReference) {
|
||||
for _, ins := range instanceNames {
|
||||
instanceLink := makeHostURL(gce.service.BasePath, gce.projectID, zone, ins)
|
||||
refs = append(refs, &compute.InstanceReference{Instance: instanceLink})
|
||||
}
|
||||
return refs
|
||||
}
|
||||
|
||||
// NodeAddresses is an implementation of Instances.NodeAddresses.
|
||||
func (gce *GCECloud) NodeAddresses(_ types.NodeName) ([]v1.NodeAddress, error) {
|
||||
internalIP, err := metadata.Get("instance/network-interfaces/0/ip")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't get internal IP: %v", err)
|
||||
}
|
||||
externalIP, err := metadata.Get("instance/network-interfaces/0/access-configs/0/external-ip")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't get external IP: %v", err)
|
||||
}
|
||||
return []v1.NodeAddress{
|
||||
{Type: v1.NodeInternalIP, Address: internalIP},
|
||||
{Type: v1.NodeExternalIP, Address: externalIP},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 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 (gce *GCECloud) NodeAddressesByProviderID(providerID string) ([]v1.NodeAddress, error) {
|
||||
project, zone, name, err := splitProviderID(providerID)
|
||||
if err != nil {
|
||||
return []v1.NodeAddress{}, err
|
||||
}
|
||||
|
||||
instance, err := gce.service.Instances.Get(project, zone, canonicalizeInstanceName(name)).Do()
|
||||
if err != nil {
|
||||
return []v1.NodeAddress{}, fmt.Errorf("error while querying for providerID %q: %v", providerID, err)
|
||||
}
|
||||
|
||||
if len(instance.NetworkInterfaces) < 1 {
|
||||
return []v1.NodeAddress{}, fmt.Errorf("could not find network interfaces for providerID %q", providerID)
|
||||
}
|
||||
networkInterface := instance.NetworkInterfaces[0]
|
||||
|
||||
nodeAddresses := []v1.NodeAddress{{Type: v1.NodeInternalIP, Address: networkInterface.NetworkIP}}
|
||||
for _, config := range networkInterface.AccessConfigs {
|
||||
nodeAddresses = append(nodeAddresses, v1.NodeAddress{Type: v1.NodeExternalIP, Address: config.NatIP})
|
||||
}
|
||||
|
||||
return nodeAddresses, nil
|
||||
}
|
||||
|
||||
// instanceByProviderID returns the cloudprovider instance of the node
|
||||
// with the specified unique providerID
|
||||
func (gce *GCECloud) instanceByProviderID(providerID string) (*gceInstance, error) {
|
||||
project, zone, name, err := splitProviderID(providerID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
instance, err := gce.getInstanceFromProjectInZoneByName(project, zone, name)
|
||||
if err != nil {
|
||||
if isHTTPErrorCode(err, http.StatusNotFound) {
|
||||
return nil, cloudprovider.InstanceNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return instance, nil
|
||||
}
|
||||
|
||||
// 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 (gce *GCECloud) InstanceTypeByProviderID(providerID string) (string, error) {
|
||||
instance, err := gce.instanceByProviderID(providerID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return instance.Type, nil
|
||||
}
|
||||
|
||||
// ExternalID returns the cloud provider ID of the node with the specified NodeName (deprecated).
|
||||
func (gce *GCECloud) ExternalID(nodeName types.NodeName) (string, error) {
|
||||
instanceName := mapNodeNameToInstanceName(nodeName)
|
||||
if gce.useMetadataServer {
|
||||
// Use metadata, if possible, to fetch ID. See issue #12000
|
||||
if gce.isCurrentInstance(instanceName) {
|
||||
externalInstanceID, err := getCurrentExternalIDViaMetadata()
|
||||
if err == nil {
|
||||
return externalInstanceID, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to GCE API call if metadata server fails to retrieve ID
|
||||
inst, err := gce.getInstanceByName(instanceName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strconv.FormatUint(inst.ID, 10), nil
|
||||
}
|
||||
|
||||
// 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 (gce *GCECloud) InstanceExistsByProviderID(providerID string) (bool, error) {
|
||||
_, err := gce.instanceByProviderID(providerID)
|
||||
if err != nil {
|
||||
if err == cloudprovider.InstanceNotFound {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// InstanceID returns the cloud provider ID of the node with the specified NodeName.
|
||||
func (gce *GCECloud) InstanceID(nodeName types.NodeName) (string, error) {
|
||||
instanceName := mapNodeNameToInstanceName(nodeName)
|
||||
if gce.useMetadataServer {
|
||||
// Use metadata, if possible, to fetch ID. See issue #12000
|
||||
if gce.isCurrentInstance(instanceName) {
|
||||
projectID, zone, err := getProjectAndZone()
|
||||
if err == nil {
|
||||
return projectID + "/" + zone + "/" + canonicalizeInstanceName(instanceName), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
instance, err := gce.getInstanceByName(instanceName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return gce.projectID + "/" + instance.Zone + "/" + instance.Name, nil
|
||||
}
|
||||
|
||||
// InstanceType returns the type of the specified node with the specified NodeName.
|
||||
func (gce *GCECloud) InstanceType(nodeName types.NodeName) (string, error) {
|
||||
instanceName := mapNodeNameToInstanceName(nodeName)
|
||||
if gce.useMetadataServer {
|
||||
// Use metadata, if possible, to fetch ID. See issue #12000
|
||||
if gce.isCurrentInstance(instanceName) {
|
||||
mType, err := getCurrentMachineTypeViaMetadata()
|
||||
if err == nil {
|
||||
return mType, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
instance, err := gce.getInstanceByName(instanceName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return instance.Type, nil
|
||||
}
|
||||
|
||||
func (gce *GCECloud) AddSSHKeyToAllInstances(user string, keyData []byte) error {
|
||||
return wait.Poll(2*time.Second, 30*time.Second, func() (bool, error) {
|
||||
project, err := gce.service.Projects.Get(gce.projectID).Do()
|
||||
if err != nil {
|
||||
glog.Errorf("Could not get project: %v", err)
|
||||
return false, nil
|
||||
}
|
||||
keyString := fmt.Sprintf("%s:%s %s@%s", user, strings.TrimSpace(string(keyData)), user, user)
|
||||
found := false
|
||||
for _, item := range project.CommonInstanceMetadata.Items {
|
||||
if item.Key == "sshKeys" {
|
||||
if strings.Contains(*item.Value, keyString) {
|
||||
// We've already added the key
|
||||
glog.Info("SSHKey already in project metadata")
|
||||
return true, nil
|
||||
}
|
||||
value := *item.Value + "\n" + keyString
|
||||
item.Value = &value
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
// This is super unlikely, so log.
|
||||
glog.Infof("Failed to find sshKeys metadata, creating a new item")
|
||||
project.CommonInstanceMetadata.Items = append(project.CommonInstanceMetadata.Items,
|
||||
&compute.MetadataItems{
|
||||
Key: "sshKeys",
|
||||
Value: &keyString,
|
||||
})
|
||||
}
|
||||
|
||||
mc := newInstancesMetricContext("add_ssh_key", "")
|
||||
op, err := gce.service.Projects.SetCommonInstanceMetadata(
|
||||
gce.projectID, project.CommonInstanceMetadata).Do()
|
||||
|
||||
if err != nil {
|
||||
glog.Errorf("Could not Set Metadata: %v", err)
|
||||
mc.Observe(err)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if err := gce.waitForGlobalOp(op, mc); err != nil {
|
||||
glog.Errorf("Could not Set Metadata: %v", err)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
glog.Infof("Successfully added sshKey to project metadata")
|
||||
return true, nil
|
||||
})
|
||||
}
|
||||
|
||||
// GetAllCurrentZones returns all the zones in which k8s nodes are currently running
|
||||
func (gce *GCECloud) GetAllCurrentZones() (sets.String, error) {
|
||||
if gce.nodeInformerSynced == nil {
|
||||
glog.Warningf("GCECloud object does not have informers set, should only happen in E2E binary.")
|
||||
return gce.GetAllZonesFromCloudProvider()
|
||||
}
|
||||
gce.nodeZonesLock.Lock()
|
||||
defer gce.nodeZonesLock.Unlock()
|
||||
if !gce.nodeInformerSynced() {
|
||||
return nil, fmt.Errorf("Node informer is not synced when trying to GetAllCurrentZones")
|
||||
}
|
||||
zones := sets.NewString()
|
||||
for zone, nodes := range gce.nodeZones {
|
||||
if len(nodes) > 0 {
|
||||
zones.Insert(zone)
|
||||
}
|
||||
}
|
||||
return zones, nil
|
||||
}
|
||||
|
||||
// GetAllZonesFromCloudProvider returns all the zones in which nodes are running
|
||||
// Only use this in E2E tests to get zones, on real clusters this will
|
||||
// get all zones with compute instances in them even if not k8s instances!!!
|
||||
// ex. I have k8s nodes in us-central1-c and us-central1-b. I also have
|
||||
// a non-k8s compute in us-central1-a. This func will return a,b, and c.
|
||||
func (gce *GCECloud) GetAllZonesFromCloudProvider() (sets.String, error) {
|
||||
zones := sets.NewString()
|
||||
|
||||
for _, zone := range gce.managedZones {
|
||||
mc := newInstancesMetricContext("list", zone)
|
||||
// We only retrieve one page in each zone - we only care about existence
|
||||
listCall := gce.service.Instances.List(gce.projectID, zone)
|
||||
|
||||
listCall = listCall.Fields("items(name)")
|
||||
res, err := listCall.Do()
|
||||
if err != nil {
|
||||
return nil, mc.Observe(err)
|
||||
}
|
||||
mc.Observe(nil)
|
||||
|
||||
if len(res.Items) != 0 {
|
||||
zones.Insert(zone)
|
||||
}
|
||||
}
|
||||
|
||||
return zones, nil
|
||||
}
|
||||
|
||||
// InsertInstance creates a new instance on GCP
|
||||
func (gce *GCECloud) InsertInstance(project string, zone string, rb *compute.Instance) error {
|
||||
mc := newInstancesMetricContext("create", zone)
|
||||
op, err := gce.service.Instances.Insert(project, zone, rb).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
return gce.waitForZoneOp(op, zone, mc)
|
||||
}
|
||||
|
||||
// ListInstanceNames returns a string of instance names seperated by spaces.
|
||||
func (gce *GCECloud) ListInstanceNames(project, zone string) (string, error) {
|
||||
res, err := gce.service.Instances.List(project, zone).Fields("items(name)").Do()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var output string
|
||||
for _, item := range res.Items {
|
||||
output += item.Name + " "
|
||||
}
|
||||
return output, nil
|
||||
}
|
||||
|
||||
// DeleteInstance deletes an instance specified by project, zone, and name
|
||||
func (gce *GCECloud) DeleteInstance(project, zone, name string) (*compute.Operation, error) {
|
||||
return gce.service.Instances.Delete(project, zone, name).Do()
|
||||
}
|
||||
|
||||
// Implementation of Instances.CurrentNodeName
|
||||
func (gce *GCECloud) CurrentNodeName(hostname string) (types.NodeName, error) {
|
||||
return types.NodeName(hostname), nil
|
||||
}
|
||||
|
||||
// AliasRanges returns a list of CIDR ranges that are assigned to the
|
||||
// `node` for allocation to pods. Returns a list of the form
|
||||
// "<ip>/<netmask>".
|
||||
func (gce *GCECloud) AliasRanges(nodeName types.NodeName) (cidrs []string, err error) {
|
||||
var instance *gceInstance
|
||||
instance, err = gce.getInstanceByName(mapNodeNameToInstanceName(nodeName))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var res *computebeta.Instance
|
||||
res, err = gce.serviceBeta.Instances.Get(
|
||||
gce.projectID, instance.Zone, instance.Name).Do()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, networkInterface := range res.NetworkInterfaces {
|
||||
for _, aliasIpRange := range networkInterface.AliasIpRanges {
|
||||
cidrs = append(cidrs, aliasIpRange.IpCidrRange)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// AddAliasToInstance adds an alias to the given instance from the named
|
||||
// secondary range.
|
||||
func (gce *GCECloud) AddAliasToInstance(nodeName types.NodeName, alias *net.IPNet) error {
|
||||
|
||||
v1instance, err := gce.getInstanceByName(mapNodeNameToInstanceName(nodeName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
instance, err := gce.serviceAlpha.Instances.Get(gce.projectID, v1instance.Zone, v1instance.Name).Do()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch len(instance.NetworkInterfaces) {
|
||||
case 0:
|
||||
return fmt.Errorf("Instance %q has no network interfaces", nodeName)
|
||||
case 1:
|
||||
default:
|
||||
glog.Warningf("Instance %q has more than one network interface, using only the first (%v)",
|
||||
nodeName, instance.NetworkInterfaces)
|
||||
}
|
||||
|
||||
iface := instance.NetworkInterfaces[0]
|
||||
iface.AliasIpRanges = append(iface.AliasIpRanges, &computealpha.AliasIpRange{
|
||||
IpCidrRange: alias.String(),
|
||||
SubnetworkRangeName: gce.secondaryRangeName,
|
||||
})
|
||||
|
||||
mc := newInstancesMetricContext("addalias", v1instance.Zone)
|
||||
op, err := gce.serviceAlpha.Instances.UpdateNetworkInterface(
|
||||
gce.projectID, lastComponent(instance.Zone), instance.Name, iface.Name, iface).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
return gce.waitForZoneOp(op, v1instance.Zone, mc)
|
||||
}
|
||||
|
||||
// Gets the named instances, returning cloudprovider.InstanceNotFound if any instance is not found
|
||||
func (gce *GCECloud) getInstancesByNames(names []string) ([]*gceInstance, error) {
|
||||
instances := make(map[string]*gceInstance)
|
||||
remaining := len(names)
|
||||
|
||||
nodeInstancePrefix := gce.nodeInstancePrefix
|
||||
for _, name := range names {
|
||||
name = canonicalizeInstanceName(name)
|
||||
if !strings.HasPrefix(name, gce.nodeInstancePrefix) {
|
||||
glog.Warningf("instance '%s' does not conform to prefix '%s', removing filter", name, gce.nodeInstancePrefix)
|
||||
nodeInstancePrefix = ""
|
||||
}
|
||||
instances[name] = nil
|
||||
}
|
||||
|
||||
for _, zone := range gce.managedZones {
|
||||
if remaining == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
pageToken := ""
|
||||
page := 0
|
||||
for ; page == 0 || (pageToken != "" && page < maxPages); page++ {
|
||||
listCall := gce.service.Instances.List(gce.projectID, zone)
|
||||
|
||||
if nodeInstancePrefix != "" {
|
||||
// Add the filter for hosts
|
||||
listCall = listCall.Filter("name eq " + nodeInstancePrefix + ".*")
|
||||
}
|
||||
|
||||
// TODO(zmerlynn): Internal bug 29524655
|
||||
// listCall = listCall.Fields("items(name,id,disks,machineType)")
|
||||
if pageToken != "" {
|
||||
listCall.PageToken(pageToken)
|
||||
}
|
||||
|
||||
res, err := listCall.Do()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pageToken = res.NextPageToken
|
||||
for _, i := range res.Items {
|
||||
name := i.Name
|
||||
if _, ok := instances[name]; !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
instance := &gceInstance{
|
||||
Zone: zone,
|
||||
Name: name,
|
||||
ID: i.Id,
|
||||
Disks: i.Disks,
|
||||
Type: lastComponent(i.MachineType),
|
||||
}
|
||||
instances[name] = instance
|
||||
remaining--
|
||||
}
|
||||
}
|
||||
if page >= maxPages {
|
||||
glog.Errorf("getInstancesByNames exceeded maxPages=%d for Instances.List: truncating.", maxPages)
|
||||
}
|
||||
}
|
||||
|
||||
instanceArray := make([]*gceInstance, len(names))
|
||||
for i, name := range names {
|
||||
name = canonicalizeInstanceName(name)
|
||||
instance := instances[name]
|
||||
if instance == nil {
|
||||
glog.Errorf("Failed to retrieve instance: %q", name)
|
||||
return nil, cloudprovider.InstanceNotFound
|
||||
}
|
||||
instanceArray[i] = instances[name]
|
||||
}
|
||||
|
||||
return instanceArray, nil
|
||||
}
|
||||
|
||||
// Gets the named instance, returning cloudprovider.InstanceNotFound if the instance is not found
|
||||
func (gce *GCECloud) getInstanceByName(name string) (*gceInstance, error) {
|
||||
// Avoid changing behaviour when not managing multiple zones
|
||||
for _, zone := range gce.managedZones {
|
||||
instance, err := gce.getInstanceFromProjectInZoneByName(gce.projectID, zone, name)
|
||||
if err != nil {
|
||||
if isHTTPErrorCode(err, http.StatusNotFound) {
|
||||
continue
|
||||
}
|
||||
glog.Errorf("getInstanceByName: failed to get instance %s in zone %s; err: %v", name, zone, err)
|
||||
return nil, err
|
||||
}
|
||||
return instance, nil
|
||||
}
|
||||
|
||||
return nil, cloudprovider.InstanceNotFound
|
||||
}
|
||||
|
||||
func (gce *GCECloud) getInstanceFromProjectInZoneByName(project, zone, name string) (*gceInstance, error) {
|
||||
name = canonicalizeInstanceName(name)
|
||||
mc := newInstancesMetricContext("get", zone)
|
||||
res, err := gce.service.Instances.Get(project, zone, name).Do()
|
||||
mc.Observe(err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &gceInstance{
|
||||
Zone: lastComponent(res.Zone),
|
||||
Name: res.Name,
|
||||
ID: res.Id,
|
||||
Disks: res.Disks,
|
||||
Type: lastComponent(res.MachineType),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getInstanceIDViaMetadata() (string, error) {
|
||||
result, err := metadata.Get("instance/hostname")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
parts := strings.Split(result, ".")
|
||||
if len(parts) == 0 {
|
||||
return "", fmt.Errorf("unexpected response: %s", result)
|
||||
}
|
||||
return parts[0], nil
|
||||
}
|
||||
|
||||
func getCurrentExternalIDViaMetadata() (string, error) {
|
||||
externalID, err := metadata.Get("instance/id")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("couldn't get external ID: %v", err)
|
||||
}
|
||||
return externalID, nil
|
||||
}
|
||||
|
||||
func getCurrentMachineTypeViaMetadata() (string, error) {
|
||||
mType, err := metadata.Get("instance/machine-type")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("couldn't get machine type: %v", err)
|
||||
}
|
||||
parts := strings.Split(mType, "/")
|
||||
if len(parts) != 4 {
|
||||
return "", fmt.Errorf("unexpected response for machine type: %s", mType)
|
||||
}
|
||||
|
||||
return parts[3], nil
|
||||
}
|
||||
|
||||
// isCurrentInstance uses metadata server to check if specified
|
||||
// instanceID matches current machine's instanceID
|
||||
func (gce *GCECloud) isCurrentInstance(instanceID string) bool {
|
||||
currentInstanceID, err := getInstanceIDViaMetadata()
|
||||
if err != nil {
|
||||
// Log and swallow error
|
||||
glog.Errorf("Failed to fetch instanceID via Metadata: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
return currentInstanceID == canonicalizeInstanceName(instanceID)
|
||||
}
|
||||
|
||||
// ComputeHostTags grabs all tags from all instances being added to the pool.
|
||||
// * The longest tag that is a prefix of the instance name is used
|
||||
// * If any instance has no matching prefix tag, return error
|
||||
// Invoking this method to get host tags is risky since it depends on the format
|
||||
// of the host names in the cluster. Only use it as a fallback if gce.nodeTags
|
||||
// is unspecified
|
||||
func (gce *GCECloud) computeHostTags(hosts []*gceInstance) ([]string, error) {
|
||||
// TODO: We could store the tags in gceInstance, so we could have already fetched it
|
||||
hostNamesByZone := make(map[string]map[string]bool) // map of zones -> map of names -> bool (for easy lookup)
|
||||
nodeInstancePrefix := gce.nodeInstancePrefix
|
||||
for _, host := range hosts {
|
||||
if !strings.HasPrefix(host.Name, gce.nodeInstancePrefix) {
|
||||
glog.Warningf("instance '%s' does not conform to prefix '%s', ignoring filter", host, gce.nodeInstancePrefix)
|
||||
nodeInstancePrefix = ""
|
||||
}
|
||||
|
||||
z, ok := hostNamesByZone[host.Zone]
|
||||
if !ok {
|
||||
z = make(map[string]bool)
|
||||
hostNamesByZone[host.Zone] = z
|
||||
}
|
||||
z[host.Name] = true
|
||||
}
|
||||
|
||||
tags := sets.NewString()
|
||||
|
||||
for zone, hostNames := range hostNamesByZone {
|
||||
pageToken := ""
|
||||
page := 0
|
||||
for ; page == 0 || (pageToken != "" && page < maxPages); page++ {
|
||||
listCall := gce.service.Instances.List(gce.projectID, zone)
|
||||
|
||||
if nodeInstancePrefix != "" {
|
||||
// Add the filter for hosts
|
||||
listCall = listCall.Filter("name eq " + nodeInstancePrefix + ".*")
|
||||
}
|
||||
|
||||
// Add the fields we want
|
||||
// TODO(zmerlynn): Internal bug 29524655
|
||||
// listCall = listCall.Fields("items(name,tags)")
|
||||
|
||||
if pageToken != "" {
|
||||
listCall = listCall.PageToken(pageToken)
|
||||
}
|
||||
|
||||
res, err := listCall.Do()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pageToken = res.NextPageToken
|
||||
for _, instance := range res.Items {
|
||||
if !hostNames[instance.Name] {
|
||||
continue
|
||||
}
|
||||
|
||||
longest_tag := ""
|
||||
for _, tag := range instance.Tags.Items {
|
||||
if strings.HasPrefix(instance.Name, tag) && len(tag) > len(longest_tag) {
|
||||
longest_tag = tag
|
||||
}
|
||||
}
|
||||
if len(longest_tag) > 0 {
|
||||
tags.Insert(longest_tag)
|
||||
} else {
|
||||
return nil, fmt.Errorf("Could not find any tag that is a prefix of instance name for instance %s", instance.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
if page >= maxPages {
|
||||
glog.Errorf("computeHostTags exceeded maxPages=%d for Instances.List: truncating.", maxPages)
|
||||
}
|
||||
}
|
||||
if len(tags) == 0 {
|
||||
return nil, fmt.Errorf("No instances found")
|
||||
}
|
||||
return tags.List(), nil
|
||||
}
|
||||
|
||||
// GetNodeTags will first try returning the list of tags specified in GCE cloud Configuration.
|
||||
// If they weren't provided, it'll compute the host tags with the given hostnames. If the list
|
||||
// of hostnames has not changed, a cached set of nodetags are returned.
|
||||
func (gce *GCECloud) GetNodeTags(nodeNames []string) ([]string, error) {
|
||||
// If nodeTags were specified through configuration, use them
|
||||
if len(gce.nodeTags) > 0 {
|
||||
return gce.nodeTags, nil
|
||||
}
|
||||
|
||||
gce.computeNodeTagLock.Lock()
|
||||
defer gce.computeNodeTagLock.Unlock()
|
||||
|
||||
// Early return if hosts have not changed
|
||||
hosts := sets.NewString(nodeNames...)
|
||||
if hosts.Equal(gce.lastKnownNodeNames) {
|
||||
return gce.lastComputedNodeTags, nil
|
||||
}
|
||||
|
||||
// Get GCE instance data by hostname
|
||||
instances, err := gce.getInstancesByNames(nodeNames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Determine list of host tags
|
||||
tags, err := gce.computeHostTags(instances)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Save the list of tags
|
||||
gce.lastKnownNodeNames = hosts
|
||||
gce.lastComputedNodeTags = tags
|
||||
return tags, nil
|
||||
}
|
61
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_interfaces.go
generated
vendored
Normal file
61
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_interfaces.go
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gce
|
||||
|
||||
import (
|
||||
computealpha "google.golang.org/api/compute/v0.alpha"
|
||||
computebeta "google.golang.org/api/compute/v0.beta"
|
||||
compute "google.golang.org/api/compute/v1"
|
||||
)
|
||||
|
||||
// These interfaces are added for testability.
|
||||
|
||||
// CloudAddressService is an interface for managing addresses
|
||||
type CloudAddressService interface {
|
||||
ReserveRegionAddress(address *compute.Address, region string) error
|
||||
GetRegionAddress(name string, region string) (*compute.Address, error)
|
||||
GetRegionAddressByIP(region, ipAddress string) (*compute.Address, error)
|
||||
DeleteRegionAddress(name, region string) error
|
||||
// TODO: Mock Global endpoints
|
||||
|
||||
// Alpha API.
|
||||
GetAlphaRegionAddress(name, region string) (*computealpha.Address, error)
|
||||
ReserveAlphaRegionAddress(addr *computealpha.Address, region string) error
|
||||
|
||||
// Beta API
|
||||
ReserveBetaRegionAddress(address *computebeta.Address, region string) error
|
||||
GetBetaRegionAddress(name string, region string) (*computebeta.Address, error)
|
||||
GetBetaRegionAddressByIP(region, ipAddress string) (*computebeta.Address, error)
|
||||
|
||||
// TODO(#51665): Remove this once the Network Tiers becomes Alpha in GCP.
|
||||
getNetworkTierFromAddress(name, region string) (string, error)
|
||||
}
|
||||
|
||||
// CloudForwardingRuleService is an interface for managing forwarding rules.
|
||||
// TODO: Expand the interface to include more methods.
|
||||
type CloudForwardingRuleService interface {
|
||||
GetRegionForwardingRule(name, region string) (*compute.ForwardingRule, error)
|
||||
CreateRegionForwardingRule(rule *compute.ForwardingRule, region string) error
|
||||
DeleteRegionForwardingRule(name, region string) error
|
||||
|
||||
// Alpha API.
|
||||
GetAlphaRegionForwardingRule(name, region string) (*computealpha.ForwardingRule, error)
|
||||
CreateAlphaRegionForwardingRule(rule *computealpha.ForwardingRule, region string) error
|
||||
|
||||
// Needed for the Alpha "Network Tiers" feature.
|
||||
getNetworkTierFromForwardingRule(name, region string) (string, error)
|
||||
}
|
204
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_loadbalancer.go
generated
vendored
Normal file
204
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_loadbalancer.go
generated
vendored
Normal file
@ -0,0 +1,204 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gce
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/kubernetes/pkg/cloudprovider"
|
||||
netsets "k8s.io/kubernetes/pkg/util/net/sets"
|
||||
)
|
||||
|
||||
type cidrs struct {
|
||||
ipn netsets.IPNet
|
||||
isSet bool
|
||||
}
|
||||
|
||||
var (
|
||||
lbSrcRngsFlag cidrs
|
||||
)
|
||||
|
||||
func newLoadBalancerMetricContext(request, region string) *metricContext {
|
||||
return newGenericMetricContext("loadbalancer", request, region, unusedMetricLabel, computeV1Version)
|
||||
}
|
||||
|
||||
type lbScheme string
|
||||
|
||||
const (
|
||||
schemeExternal lbScheme = "EXTERNAL"
|
||||
schemeInternal lbScheme = "INTERNAL"
|
||||
)
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
// LB L7 proxies and all L3/4/7 health checkers have client addresses within these known CIDRs.
|
||||
lbSrcRngsFlag.ipn, err = netsets.ParseIPNets([]string{"130.211.0.0/22", "35.191.0.0/16", "209.85.152.0/22", "209.85.204.0/22"}...)
|
||||
if err != nil {
|
||||
panic("Incorrect default GCE L7 source ranges")
|
||||
}
|
||||
|
||||
flag.Var(&lbSrcRngsFlag, "cloud-provider-gce-lb-src-cidrs", "CIDRs opened in GCE firewall for LB traffic proxy & health checks")
|
||||
}
|
||||
|
||||
// String is the method to format the flag's value, part of the flag.Value interface.
|
||||
func (c *cidrs) String() string {
|
||||
return strings.Join(c.ipn.StringSlice(), ",")
|
||||
}
|
||||
|
||||
// Set supports a value of CSV or the flag repeated multiple times
|
||||
func (c *cidrs) Set(value string) error {
|
||||
// On first Set(), clear the original defaults
|
||||
if !c.isSet {
|
||||
c.isSet = true
|
||||
c.ipn = make(netsets.IPNet)
|
||||
} else {
|
||||
return fmt.Errorf("GCE LB CIDRs have already been set")
|
||||
}
|
||||
|
||||
for _, cidr := range strings.Split(value, ",") {
|
||||
_, ipnet, err := net.ParseCIDR(cidr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.ipn.Insert(ipnet)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadBalancerSrcRanges contains the ranges of ips used by the GCE load balancers (l4 & L7)
|
||||
// for proxying client requests and performing health checks.
|
||||
func LoadBalancerSrcRanges() []string {
|
||||
return lbSrcRngsFlag.ipn.StringSlice()
|
||||
}
|
||||
|
||||
// GetLoadBalancer is an implementation of LoadBalancer.GetLoadBalancer
|
||||
func (gce *GCECloud) GetLoadBalancer(clusterName string, svc *v1.Service) (*v1.LoadBalancerStatus, bool, error) {
|
||||
loadBalancerName := cloudprovider.GetLoadBalancerName(svc)
|
||||
fwd, err := gce.GetRegionForwardingRule(loadBalancerName, gce.region)
|
||||
if err == nil {
|
||||
status := &v1.LoadBalancerStatus{}
|
||||
status.Ingress = []v1.LoadBalancerIngress{{IP: fwd.IPAddress}}
|
||||
|
||||
return status, true, nil
|
||||
}
|
||||
return nil, false, ignoreNotFound(err)
|
||||
}
|
||||
|
||||
// EnsureLoadBalancer is an implementation of LoadBalancer.EnsureLoadBalancer.
|
||||
func (gce *GCECloud) EnsureLoadBalancer(clusterName string, svc *v1.Service, nodes []*v1.Node) (*v1.LoadBalancerStatus, error) {
|
||||
loadBalancerName := cloudprovider.GetLoadBalancerName(svc)
|
||||
desiredScheme := getSvcScheme(svc)
|
||||
clusterID, err := gce.ClusterID.GetID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
glog.V(4).Infof("EnsureLoadBalancer(%v, %v, %v, %v, %v): ensure %v loadbalancer", clusterName, svc.Namespace, svc.Name, loadBalancerName, gce.region, desiredScheme)
|
||||
|
||||
existingFwdRule, err := gce.GetRegionForwardingRule(loadBalancerName, gce.region)
|
||||
if err != nil && !isNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if existingFwdRule != nil {
|
||||
existingScheme := lbScheme(strings.ToUpper(existingFwdRule.LoadBalancingScheme))
|
||||
|
||||
// If the loadbalancer type changes between INTERNAL and EXTERNAL, the old load balancer should be deleted.
|
||||
if existingScheme != desiredScheme {
|
||||
glog.V(4).Infof("EnsureLoadBalancer(%v, %v, %v, %v, %v): deleting existing %v loadbalancer", clusterName, svc.Namespace, svc.Name, loadBalancerName, gce.region, existingScheme)
|
||||
switch existingScheme {
|
||||
case schemeInternal:
|
||||
err = gce.ensureInternalLoadBalancerDeleted(clusterName, clusterID, svc)
|
||||
default:
|
||||
err = gce.ensureExternalLoadBalancerDeleted(clusterName, clusterID, svc)
|
||||
}
|
||||
glog.V(4).Infof("EnsureLoadBalancer(%v, %v, %v, %v, %v): done deleting existing %v loadbalancer. err: %v", clusterName, svc.Namespace, svc.Name, loadBalancerName, gce.region, existingScheme, err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Assume the ensureDeleted function successfully deleted the forwarding rule.
|
||||
existingFwdRule = nil
|
||||
}
|
||||
}
|
||||
|
||||
var status *v1.LoadBalancerStatus
|
||||
switch desiredScheme {
|
||||
case schemeInternal:
|
||||
status, err = gce.ensureInternalLoadBalancer(clusterName, clusterID, svc, existingFwdRule, nodes)
|
||||
default:
|
||||
status, err = gce.ensureExternalLoadBalancer(clusterName, clusterID, svc, existingFwdRule, nodes)
|
||||
}
|
||||
glog.V(4).Infof("EnsureLoadBalancer(%v, %v, %v, %v, %v): done ensuring loadbalancer. err: %v", clusterName, svc.Namespace, svc.Name, loadBalancerName, gce.region, err)
|
||||
return status, err
|
||||
}
|
||||
|
||||
// UpdateLoadBalancer is an implementation of LoadBalancer.UpdateLoadBalancer.
|
||||
func (gce *GCECloud) UpdateLoadBalancer(clusterName string, svc *v1.Service, nodes []*v1.Node) error {
|
||||
loadBalancerName := cloudprovider.GetLoadBalancerName(svc)
|
||||
scheme := getSvcScheme(svc)
|
||||
clusterID, err := gce.ClusterID.GetID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
glog.V(4).Infof("UpdateLoadBalancer(%v, %v, %v, %v, %v): updating with %d nodes", clusterName, svc.Namespace, svc.Name, loadBalancerName, gce.region, len(nodes))
|
||||
|
||||
switch scheme {
|
||||
case schemeInternal:
|
||||
err = gce.updateInternalLoadBalancer(clusterName, clusterID, svc, nodes)
|
||||
default:
|
||||
err = gce.updateExternalLoadBalancer(clusterName, svc, nodes)
|
||||
}
|
||||
glog.V(4).Infof("UpdateLoadBalancer(%v, %v, %v, %v, %v): done updating. err: %v", clusterName, svc.Namespace, svc.Name, loadBalancerName, gce.region, err)
|
||||
return err
|
||||
}
|
||||
|
||||
// EnsureLoadBalancerDeleted is an implementation of LoadBalancer.EnsureLoadBalancerDeleted.
|
||||
func (gce *GCECloud) EnsureLoadBalancerDeleted(clusterName string, svc *v1.Service) error {
|
||||
loadBalancerName := cloudprovider.GetLoadBalancerName(svc)
|
||||
scheme := getSvcScheme(svc)
|
||||
clusterID, err := gce.ClusterID.GetID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
glog.V(4).Infof("EnsureLoadBalancerDeleted(%v, %v, %v, %v, %v): deleting loadbalancer", clusterName, svc.Namespace, svc.Name, loadBalancerName, gce.region)
|
||||
|
||||
switch scheme {
|
||||
case schemeInternal:
|
||||
err = gce.ensureInternalLoadBalancerDeleted(clusterName, clusterID, svc)
|
||||
default:
|
||||
err = gce.ensureExternalLoadBalancerDeleted(clusterName, clusterID, svc)
|
||||
}
|
||||
glog.V(4).Infof("EnsureLoadBalancerDeleted(%v, %v, %v, %v, %v): done deleting loadbalancer. err: %v", clusterName, svc.Namespace, svc.Name, loadBalancerName, gce.region, err)
|
||||
return err
|
||||
}
|
||||
|
||||
func getSvcScheme(svc *v1.Service) lbScheme {
|
||||
if typ, ok := GetLoadBalancerAnnotationType(svc); ok && typ == LBTypeInternal {
|
||||
return schemeInternal
|
||||
}
|
||||
return schemeExternal
|
||||
}
|
1079
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_loadbalancer_external.go
generated
vendored
Normal file
1079
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_loadbalancer_external.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
239
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_loadbalancer_external_test.go
generated
vendored
Normal file
239
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_loadbalancer_external_test.go
generated
vendored
Normal file
@ -0,0 +1,239 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gce
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
computealpha "google.golang.org/api/compute/v0.alpha"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func TestEnsureStaticIP(t *testing.T) {
|
||||
fcas := NewFakeCloudAddressService()
|
||||
ipName := "some-static-ip"
|
||||
serviceName := ""
|
||||
region := "us-central1"
|
||||
|
||||
// First ensure call
|
||||
ip, existed, err := ensureStaticIP(fcas, ipName, serviceName, region, "", NetworkTierDefault)
|
||||
if err != nil || existed || ip == "" {
|
||||
t.Fatalf(`ensureStaticIP(%v, %v, %v, %v, "") = %v, %v, %v; want valid ip, false, nil`, fcas, ipName, serviceName, region, ip, existed, err)
|
||||
}
|
||||
|
||||
// Second ensure call
|
||||
var ipPrime string
|
||||
ipPrime, existed, err = ensureStaticIP(fcas, ipName, serviceName, region, ip, NetworkTierDefault)
|
||||
if err != nil || !existed || ip != ipPrime {
|
||||
t.Fatalf(`ensureStaticIP(%v, %v, %v, %v, %v) = %v, %v, %v; want %v, true, nil`, fcas, ipName, serviceName, region, ip, ipPrime, existed, err, ip)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnsureStaticIPWithTier(t *testing.T) {
|
||||
s := NewFakeCloudAddressService()
|
||||
serviceName := ""
|
||||
region := "us-east1"
|
||||
|
||||
for desc, tc := range map[string]struct {
|
||||
name string
|
||||
netTier NetworkTier
|
||||
expected string
|
||||
}{
|
||||
"Premium (default)": {
|
||||
name: "foo-1",
|
||||
netTier: NetworkTierPremium,
|
||||
expected: "PREMIUM",
|
||||
},
|
||||
"Standard": {
|
||||
name: "foo-2",
|
||||
netTier: NetworkTierStandard,
|
||||
expected: "STANDARD",
|
||||
},
|
||||
} {
|
||||
t.Run(desc, func(t *testing.T) {
|
||||
ip, existed, err := ensureStaticIP(s, tc.name, serviceName, region, "", tc.netTier)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, existed)
|
||||
assert.NotEqual(t, "", ip)
|
||||
// Get the Address from the fake address service and verify that the tier
|
||||
// is set correctly.
|
||||
alphaAddr, err := s.GetAlphaRegionAddress(tc.name, region)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tc.expected, alphaAddr.NetworkTier)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyRequestedIP(t *testing.T) {
|
||||
region := "test-region"
|
||||
lbRef := "test-lb"
|
||||
s := NewFakeCloudAddressService()
|
||||
|
||||
for desc, tc := range map[string]struct {
|
||||
requestedIP string
|
||||
fwdRuleIP string
|
||||
netTier NetworkTier
|
||||
addrList []*computealpha.Address
|
||||
expectErr bool
|
||||
expectUserOwned bool
|
||||
}{
|
||||
"requested IP exists": {
|
||||
requestedIP: "1.1.1.1",
|
||||
netTier: NetworkTierPremium,
|
||||
addrList: []*computealpha.Address{{Name: "foo", Address: "1.1.1.1", NetworkTier: "PREMIUM"}},
|
||||
expectErr: false,
|
||||
expectUserOwned: true,
|
||||
},
|
||||
"requested IP is not static, but is in use by the fwd rule": {
|
||||
requestedIP: "1.1.1.1",
|
||||
fwdRuleIP: "1.1.1.1",
|
||||
netTier: NetworkTierPremium,
|
||||
expectErr: false,
|
||||
},
|
||||
"requested IP is not static and is not used by the fwd rule": {
|
||||
requestedIP: "1.1.1.1",
|
||||
fwdRuleIP: "2.2.2.2",
|
||||
netTier: NetworkTierPremium,
|
||||
expectErr: true,
|
||||
},
|
||||
"no requested IP": {
|
||||
netTier: NetworkTierPremium,
|
||||
expectErr: false,
|
||||
},
|
||||
"requested IP exists, but network tier does not match": {
|
||||
requestedIP: "1.1.1.1",
|
||||
netTier: NetworkTierStandard,
|
||||
addrList: []*computealpha.Address{{Name: "foo", Address: "1.1.1.1", NetworkTier: "PREMIUM"}},
|
||||
expectErr: true,
|
||||
},
|
||||
} {
|
||||
t.Run(desc, func(t *testing.T) {
|
||||
s.SetRegionalAddresses(region, tc.addrList)
|
||||
isUserOwnedIP, err := verifyUserRequestedIP(s, region, tc.requestedIP, tc.fwdRuleIP, lbRef, tc.netTier)
|
||||
assert.Equal(t, tc.expectErr, err != nil, fmt.Sprintf("err: %v", err))
|
||||
assert.Equal(t, tc.expectUserOwned, isUserOwnedIP)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateForwardingRuleWithTier(t *testing.T) {
|
||||
s := NewFakeCloudForwardingRuleService()
|
||||
// Common variables among the tests.
|
||||
ports := []v1.ServicePort{{Name: "foo", Protocol: v1.ProtocolTCP, Port: int32(123)}}
|
||||
region := "test-region"
|
||||
target := "test-target-pool"
|
||||
svcName := "foo-svc"
|
||||
|
||||
for desc, tc := range map[string]struct {
|
||||
netTier NetworkTier
|
||||
expectedRule *computealpha.ForwardingRule
|
||||
}{
|
||||
"Premium tier": {
|
||||
netTier: NetworkTierPremium,
|
||||
expectedRule: &computealpha.ForwardingRule{
|
||||
Name: "lb-1",
|
||||
Description: `{"kubernetes.io/service-name":"foo-svc"}`,
|
||||
IPAddress: "1.1.1.1",
|
||||
IPProtocol: "TCP",
|
||||
PortRange: "123-123",
|
||||
Target: target,
|
||||
NetworkTier: "PREMIUM",
|
||||
},
|
||||
},
|
||||
"Standard tier": {
|
||||
netTier: NetworkTierStandard,
|
||||
expectedRule: &computealpha.ForwardingRule{
|
||||
Name: "lb-2",
|
||||
Description: `{"kubernetes.io/service-name":"foo-svc"}`,
|
||||
IPAddress: "2.2.2.2",
|
||||
IPProtocol: "TCP",
|
||||
PortRange: "123-123",
|
||||
Target: target,
|
||||
NetworkTier: "STANDARD",
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(desc, func(t *testing.T) {
|
||||
lbName := tc.expectedRule.Name
|
||||
ipAddr := tc.expectedRule.IPAddress
|
||||
|
||||
err := createForwardingRule(s, lbName, svcName, region, ipAddr, target, ports, tc.netTier)
|
||||
assert.NoError(t, err)
|
||||
|
||||
alphaRule, err := s.GetAlphaRegionForwardingRule(lbName, region)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tc.expectedRule, alphaRule)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteAddressWithWrongTier(t *testing.T) {
|
||||
region := "test-region"
|
||||
lbRef := "test-lb"
|
||||
s := NewFakeCloudAddressService()
|
||||
|
||||
for desc, tc := range map[string]struct {
|
||||
addrName string
|
||||
netTier NetworkTier
|
||||
addrList []*computealpha.Address
|
||||
expectDelete bool
|
||||
}{
|
||||
"Network tiers (premium) match; do nothing": {
|
||||
addrName: "foo1",
|
||||
netTier: NetworkTierPremium,
|
||||
addrList: []*computealpha.Address{{Name: "foo1", Address: "1.1.1.1", NetworkTier: "PREMIUM"}},
|
||||
},
|
||||
"Network tiers (standard) match; do nothing": {
|
||||
addrName: "foo2",
|
||||
netTier: NetworkTierStandard,
|
||||
addrList: []*computealpha.Address{{Name: "foo2", Address: "1.1.1.2", NetworkTier: "STANDARD"}},
|
||||
},
|
||||
"Wrong network tier (standard); delete address": {
|
||||
addrName: "foo3",
|
||||
netTier: NetworkTierPremium,
|
||||
addrList: []*computealpha.Address{{Name: "foo3", Address: "1.1.1.3", NetworkTier: "STANDARD"}},
|
||||
expectDelete: true,
|
||||
},
|
||||
"Wrong network tier (preimium); delete address": {
|
||||
addrName: "foo4",
|
||||
netTier: NetworkTierStandard,
|
||||
addrList: []*computealpha.Address{{Name: "foo4", Address: "1.1.1.4", NetworkTier: "PREMIUM"}},
|
||||
expectDelete: true,
|
||||
},
|
||||
} {
|
||||
t.Run(desc, func(t *testing.T) {
|
||||
s.SetRegionalAddresses(region, tc.addrList)
|
||||
// Sanity check to ensure we inject the right address.
|
||||
_, err := s.GetRegionAddress(tc.addrName, region)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = deleteAddressWithWrongTier(s, region, tc.addrName, lbRef, tc.netTier)
|
||||
assert.NoError(t, err)
|
||||
// Check whether the address still exists.
|
||||
_, err = s.GetRegionAddress(tc.addrName, region)
|
||||
if tc.expectDelete {
|
||||
assert.True(t, isNotFound(err))
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
698
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_loadbalancer_internal.go
generated
vendored
Normal file
698
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_loadbalancer_internal.go
generated
vendored
Normal file
@ -0,0 +1,698 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gce
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
compute "google.golang.org/api/compute/v1"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
v1_service "k8s.io/kubernetes/pkg/api/v1/service"
|
||||
"k8s.io/kubernetes/pkg/cloudprovider"
|
||||
)
|
||||
|
||||
const (
|
||||
allInstances = "ALL"
|
||||
)
|
||||
|
||||
func (gce *GCECloud) ensureInternalLoadBalancer(clusterName, clusterID string, svc *v1.Service, existingFwdRule *compute.ForwardingRule, nodes []*v1.Node) (*v1.LoadBalancerStatus, error) {
|
||||
nm := types.NamespacedName{Name: svc.Name, Namespace: svc.Namespace}
|
||||
ports, protocol := getPortsAndProtocol(svc.Spec.Ports)
|
||||
scheme := schemeInternal
|
||||
loadBalancerName := cloudprovider.GetLoadBalancerName(svc)
|
||||
sharedBackend := shareBackendService(svc)
|
||||
backendServiceName := makeBackendServiceName(loadBalancerName, clusterID, sharedBackend, scheme, protocol, svc.Spec.SessionAffinity)
|
||||
backendServiceLink := gce.getBackendServiceLink(backendServiceName)
|
||||
|
||||
// Ensure instance groups exist and nodes are assigned to groups
|
||||
igName := makeInstanceGroupName(clusterID)
|
||||
igLinks, err := gce.ensureInternalInstanceGroups(igName, nodes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get existing backend service (if exists)
|
||||
var existingBackendService *compute.BackendService
|
||||
if existingFwdRule != nil && existingFwdRule.BackendService != "" {
|
||||
existingBSName := getNameFromLink(existingFwdRule.BackendService)
|
||||
if existingBackendService, err = gce.GetRegionBackendService(existingBSName, gce.region); err != nil && !isNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Lock the sharedResourceLock to prevent any deletions of shared resources while assembling shared resources here
|
||||
gce.sharedResourceLock.Lock()
|
||||
defer gce.sharedResourceLock.Unlock()
|
||||
|
||||
// Ensure health check exists before creating the backend service. The health check is shared
|
||||
// if externalTrafficPolicy=Cluster.
|
||||
sharedHealthCheck := !v1_service.RequestsOnlyLocalTraffic(svc)
|
||||
hcName := makeHealthCheckName(loadBalancerName, clusterID, sharedHealthCheck)
|
||||
hcPath, hcPort := GetNodesHealthCheckPath(), GetNodesHealthCheckPort()
|
||||
if !sharedHealthCheck {
|
||||
// Service requires a special health check, retrieve the OnlyLocal port & path
|
||||
hcPath, hcPort = v1_service.GetServiceHealthCheckPathPort(svc)
|
||||
}
|
||||
hc, err := gce.ensureInternalHealthCheck(hcName, nm, sharedHealthCheck, hcPath, hcPort)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Determine IP which will be used for this LB. If no forwarding rule has been established
|
||||
// or specified in the Service spec, then requestedIP = "".
|
||||
requestedIP := determineRequestedIP(svc, existingFwdRule)
|
||||
ipToUse := requestedIP
|
||||
|
||||
var addrMgr *addressManager
|
||||
// If the network is not a legacy network, use the address manager
|
||||
if !gce.IsLegacyNetwork() {
|
||||
addrMgr = newAddressManager(gce, nm.String(), gce.Region(), gce.SubnetworkURL(), loadBalancerName, requestedIP, schemeInternal)
|
||||
ipToUse, err = addrMgr.HoldAddress()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
glog.V(2).Infof("ensureInternalLoadBalancer(%v): reserved IP %q for the forwarding rule", loadBalancerName, ipToUse)
|
||||
}
|
||||
|
||||
// Ensure firewall rules if necessary
|
||||
if err = gce.ensureInternalFirewalls(loadBalancerName, ipToUse, clusterID, nm, svc, strconv.Itoa(int(hcPort)), sharedHealthCheck, nodes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
expectedFwdRule := &compute.ForwardingRule{
|
||||
Name: loadBalancerName,
|
||||
Description: fmt.Sprintf(`{"kubernetes.io/service-name":"%s"}`, nm.String()),
|
||||
IPAddress: ipToUse,
|
||||
BackendService: backendServiceLink,
|
||||
Ports: ports,
|
||||
IPProtocol: string(protocol),
|
||||
LoadBalancingScheme: string(scheme),
|
||||
}
|
||||
|
||||
// Specify subnetwork if known
|
||||
if len(gce.subnetworkURL) > 0 {
|
||||
expectedFwdRule.Subnetwork = gce.subnetworkURL
|
||||
} else {
|
||||
expectedFwdRule.Network = gce.networkURL
|
||||
}
|
||||
|
||||
fwdRuleDeleted := false
|
||||
if existingFwdRule != nil && !fwdRuleEqual(existingFwdRule, expectedFwdRule) {
|
||||
glog.V(2).Infof("ensureInternalLoadBalancer(%v): deleting existing forwarding rule with IP address %v", loadBalancerName, existingFwdRule.IPAddress)
|
||||
if err = ignoreNotFound(gce.DeleteRegionForwardingRule(loadBalancerName, gce.region)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fwdRuleDeleted = true
|
||||
}
|
||||
|
||||
bsDescription := makeBackendServiceDescription(nm, sharedBackend)
|
||||
err = gce.ensureInternalBackendService(backendServiceName, bsDescription, svc.Spec.SessionAffinity, scheme, protocol, igLinks, hc.SelfLink)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If we previously deleted the forwarding rule or it never existed, finally create it.
|
||||
if fwdRuleDeleted || existingFwdRule == nil {
|
||||
glog.V(2).Infof("ensureInternalLoadBalancer(%v): creating forwarding rule", loadBalancerName)
|
||||
if err = gce.CreateRegionForwardingRule(expectedFwdRule, gce.region); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
glog.V(2).Infof("ensureInternalLoadBalancer(%v): created forwarding rule", loadBalancerName)
|
||||
}
|
||||
|
||||
// Delete the previous internal load balancer resources if necessary
|
||||
if existingBackendService != nil {
|
||||
gce.clearPreviousInternalResources(svc, loadBalancerName, existingBackendService, backendServiceName, hcName)
|
||||
}
|
||||
|
||||
if addrMgr != nil {
|
||||
// Now that the controller knows the forwarding rule exists, we can release the address.
|
||||
if err := addrMgr.ReleaseAddress(); err != nil {
|
||||
glog.Errorf("ensureInternalLoadBalancer: failed to release address reservation, possibly causing an orphan: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Get the most recent forwarding rule for the address.
|
||||
updatedFwdRule, err := gce.GetRegionForwardingRule(loadBalancerName, gce.region)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
status := &v1.LoadBalancerStatus{}
|
||||
status.Ingress = []v1.LoadBalancerIngress{{IP: updatedFwdRule.IPAddress}}
|
||||
return status, nil
|
||||
}
|
||||
|
||||
func (gce *GCECloud) clearPreviousInternalResources(svc *v1.Service, loadBalancerName string, existingBackendService *compute.BackendService, expectedBSName, expectedHCName string) {
|
||||
// If a new backend service was created, delete the old one.
|
||||
if existingBackendService.Name != expectedBSName {
|
||||
glog.V(2).Infof("clearPreviousInternalResources(%v): expected backend service %q does not match previous %q - deleting backend service", loadBalancerName, expectedBSName, existingBackendService.Name)
|
||||
if err := gce.teardownInternalBackendService(existingBackendService.Name); err != nil && !isNotFound(err) {
|
||||
glog.Warningf("clearPreviousInternalResources: could not delete old backend service: %v, err: %v", existingBackendService.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
// If a new health check was created, delete the old one.
|
||||
if len(existingBackendService.HealthChecks) == 1 {
|
||||
existingHCName := getNameFromLink(existingBackendService.HealthChecks[0])
|
||||
if existingHCName != expectedHCName {
|
||||
glog.V(2).Infof("clearPreviousInternalResources(%v): expected health check %q does not match previous %q - deleting health check", loadBalancerName, expectedHCName, existingHCName)
|
||||
if err := gce.teardownInternalHealthCheckAndFirewall(svc, existingHCName); err != nil {
|
||||
glog.Warningf("clearPreviousInternalResources: could not delete existing healthcheck: %v, err: %v", existingHCName, err)
|
||||
}
|
||||
}
|
||||
} else if len(existingBackendService.HealthChecks) > 1 {
|
||||
glog.Warningf("clearPreviousInternalResources(%v): more than one health check on the backend service %v, %v", loadBalancerName, existingBackendService.Name, existingBackendService.HealthChecks)
|
||||
}
|
||||
}
|
||||
|
||||
// updateInternalLoadBalancer is called when the list of nodes has changed. Therefore, only the instance groups
|
||||
// and possibly the backend service need to be updated.
|
||||
func (gce *GCECloud) updateInternalLoadBalancer(clusterName, clusterID string, svc *v1.Service, nodes []*v1.Node) error {
|
||||
gce.sharedResourceLock.Lock()
|
||||
defer gce.sharedResourceLock.Unlock()
|
||||
|
||||
igName := makeInstanceGroupName(clusterID)
|
||||
igLinks, err := gce.ensureInternalInstanceGroups(igName, nodes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Generate the backend service name
|
||||
_, protocol := getPortsAndProtocol(svc.Spec.Ports)
|
||||
scheme := schemeInternal
|
||||
loadBalancerName := cloudprovider.GetLoadBalancerName(svc)
|
||||
backendServiceName := makeBackendServiceName(loadBalancerName, clusterID, shareBackendService(svc), scheme, protocol, svc.Spec.SessionAffinity)
|
||||
// Ensure the backend service has the proper backend/instance-group links
|
||||
return gce.ensureInternalBackendServiceGroups(backendServiceName, igLinks)
|
||||
}
|
||||
|
||||
func (gce *GCECloud) ensureInternalLoadBalancerDeleted(clusterName, clusterID string, svc *v1.Service) error {
|
||||
loadBalancerName := cloudprovider.GetLoadBalancerName(svc)
|
||||
_, protocol := getPortsAndProtocol(svc.Spec.Ports)
|
||||
scheme := schemeInternal
|
||||
sharedBackend := shareBackendService(svc)
|
||||
sharedHealthCheck := !v1_service.RequestsOnlyLocalTraffic(svc)
|
||||
|
||||
gce.sharedResourceLock.Lock()
|
||||
defer gce.sharedResourceLock.Unlock()
|
||||
|
||||
glog.V(2).Infof("ensureInternalLoadBalancerDeleted(%v): attempting delete of region internal address", loadBalancerName)
|
||||
ensureAddressDeleted(gce, loadBalancerName, gce.region)
|
||||
|
||||
glog.V(2).Infof("ensureInternalLoadBalancerDeleted(%v): deleting region internal forwarding rule", loadBalancerName)
|
||||
if err := ignoreNotFound(gce.DeleteRegionForwardingRule(loadBalancerName, gce.region)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
backendServiceName := makeBackendServiceName(loadBalancerName, clusterID, sharedBackend, scheme, protocol, svc.Spec.SessionAffinity)
|
||||
glog.V(2).Infof("ensureInternalLoadBalancerDeleted(%v): deleting region backend service %v", loadBalancerName, backendServiceName)
|
||||
if err := gce.teardownInternalBackendService(backendServiceName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
glog.V(2).Infof("ensureInternalLoadBalancerDeleted(%v): deleting firewall for traffic", loadBalancerName)
|
||||
if err := ignoreNotFound(gce.DeleteFirewall(loadBalancerName)); err != nil {
|
||||
if isForbidden(err) && gce.OnXPN() {
|
||||
glog.V(2).Infof("ensureInternalLoadBalancerDeleted(%v): could not delete traffic firewall on XPN cluster. Raising event.", loadBalancerName)
|
||||
gce.raiseFirewallChangeNeededEvent(svc, FirewallToGCloudDeleteCmd(loadBalancerName, gce.NetworkProjectID()))
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
hcName := makeHealthCheckName(loadBalancerName, clusterID, sharedHealthCheck)
|
||||
glog.V(2).Infof("ensureInternalLoadBalancerDeleted(%v): deleting health check %v and its firewall", loadBalancerName, hcName)
|
||||
if err := gce.teardownInternalHealthCheckAndFirewall(svc, hcName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Try deleting instance groups - expect ResourceInuse error if needed by other LBs
|
||||
igName := makeInstanceGroupName(clusterID)
|
||||
if err := gce.ensureInternalInstanceGroupsDeleted(igName); err != nil && !isInUsedByError(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gce *GCECloud) teardownInternalBackendService(bsName string) error {
|
||||
if err := gce.DeleteRegionBackendService(bsName, gce.region); err != nil {
|
||||
if isNotFound(err) {
|
||||
glog.V(2).Infof("teardownInternalBackendService(%v): backend service already deleted. err: %v", bsName, err)
|
||||
return nil
|
||||
} else if isInUsedByError(err) {
|
||||
glog.V(2).Infof("teardownInternalBackendService(%v): backend service in use.", bsName)
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf("failed to delete backend service: %v, err: %v", bsName, err)
|
||||
}
|
||||
}
|
||||
glog.V(2).Infof("teardownInternalBackendService(%v): backend service deleted", bsName)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gce *GCECloud) teardownInternalHealthCheckAndFirewall(svc *v1.Service, hcName string) error {
|
||||
if err := gce.DeleteHealthCheck(hcName); err != nil {
|
||||
if isNotFound(err) {
|
||||
glog.V(2).Infof("teardownInternalHealthCheckAndFirewall(%v): health check does not exist.", hcName)
|
||||
// Purposely do not early return - double check the firewall does not exist
|
||||
} else if isInUsedByError(err) {
|
||||
glog.V(2).Infof("teardownInternalHealthCheckAndFirewall(%v): health check in use.", hcName)
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf("failed to delete health check: %v, err: %v", hcName, err)
|
||||
}
|
||||
}
|
||||
glog.V(2).Infof("teardownInternalHealthCheckAndFirewall(%v): health check deleted", hcName)
|
||||
|
||||
hcFirewallName := makeHealthCheckFirewallNameFromHC(hcName)
|
||||
if err := ignoreNotFound(gce.DeleteFirewall(hcFirewallName)); err != nil {
|
||||
if isForbidden(err) && gce.OnXPN() {
|
||||
glog.V(2).Infof("teardownInternalHealthCheckAndFirewall(%v): could not delete health check traffic firewall on XPN cluster. Raising Event.", hcName)
|
||||
gce.raiseFirewallChangeNeededEvent(svc, FirewallToGCloudDeleteCmd(hcFirewallName, gce.NetworkProjectID()))
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("failed to delete health check firewall: %v, err: %v", hcFirewallName, err)
|
||||
}
|
||||
glog.V(2).Infof("teardownInternalHealthCheckAndFirewall(%v): health check firewall deleted", hcFirewallName)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gce *GCECloud) ensureInternalFirewall(svc *v1.Service, fwName, fwDesc string, sourceRanges []string, ports []string, protocol v1.Protocol, nodes []*v1.Node) error {
|
||||
glog.V(2).Infof("ensureInternalFirewall(%v): checking existing firewall", fwName)
|
||||
targetTags, err := gce.GetNodeTags(nodeNames(nodes))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
existingFirewall, err := gce.GetFirewall(fwName)
|
||||
if err != nil && !isNotFound(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
expectedFirewall := &compute.Firewall{
|
||||
Name: fwName,
|
||||
Description: fwDesc,
|
||||
Network: gce.networkURL,
|
||||
SourceRanges: sourceRanges,
|
||||
TargetTags: targetTags,
|
||||
Allowed: []*compute.FirewallAllowed{
|
||||
{
|
||||
IPProtocol: strings.ToLower(string(protocol)),
|
||||
Ports: ports,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if existingFirewall == nil {
|
||||
glog.V(2).Infof("ensureInternalFirewall(%v): creating firewall", fwName)
|
||||
err = gce.CreateFirewall(expectedFirewall)
|
||||
if err != nil && isForbidden(err) && gce.OnXPN() {
|
||||
glog.V(2).Infof("ensureInternalFirewall(%v): do not have permission to create firewall rule (on XPN). Raising event.", fwName)
|
||||
gce.raiseFirewallChangeNeededEvent(svc, FirewallToGCloudCreateCmd(expectedFirewall, gce.NetworkProjectID()))
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if firewallRuleEqual(expectedFirewall, existingFirewall) {
|
||||
return nil
|
||||
}
|
||||
|
||||
glog.V(2).Infof("ensureInternalFirewall(%v): updating firewall", fwName)
|
||||
err = gce.UpdateFirewall(expectedFirewall)
|
||||
if err != nil && isForbidden(err) && gce.OnXPN() {
|
||||
glog.V(2).Infof("ensureInternalFirewall(%v): do not have permission to update firewall rule (on XPN). Raising event.", fwName)
|
||||
gce.raiseFirewallChangeNeededEvent(svc, FirewallToGCloudUpdateCmd(expectedFirewall, gce.NetworkProjectID()))
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (gce *GCECloud) ensureInternalFirewalls(loadBalancerName, ipAddress, clusterID string, nm types.NamespacedName, svc *v1.Service, healthCheckPort string, sharedHealthCheck bool, nodes []*v1.Node) error {
|
||||
// First firewall is for ingress traffic
|
||||
fwDesc := makeFirewallDescription(nm.String(), ipAddress)
|
||||
ports, protocol := getPortsAndProtocol(svc.Spec.Ports)
|
||||
sourceRanges, err := v1_service.GetLoadBalancerSourceRanges(svc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = gce.ensureInternalFirewall(svc, loadBalancerName, fwDesc, sourceRanges.StringSlice(), ports, protocol, nodes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Second firewall is for health checking nodes / services
|
||||
fwHCName := makeHealthCheckFirewallName(loadBalancerName, clusterID, sharedHealthCheck)
|
||||
hcSrcRanges := LoadBalancerSrcRanges()
|
||||
return gce.ensureInternalFirewall(svc, fwHCName, "", hcSrcRanges, []string{healthCheckPort}, v1.ProtocolTCP, nodes)
|
||||
}
|
||||
|
||||
func (gce *GCECloud) ensureInternalHealthCheck(name string, svcName types.NamespacedName, shared bool, path string, port int32) (*compute.HealthCheck, error) {
|
||||
glog.V(2).Infof("ensureInternalHealthCheck(%v, %v, %v): checking existing health check", name, path, port)
|
||||
expectedHC := newInternalLBHealthCheck(name, svcName, shared, path, port)
|
||||
|
||||
hc, err := gce.GetHealthCheck(name)
|
||||
if err != nil && !isNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if hc == nil {
|
||||
glog.V(2).Infof("ensureInternalHealthCheck: did not find health check %v, creating one with port %v path %v", name, port, path)
|
||||
if err = gce.CreateHealthCheck(expectedHC); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hc, err = gce.GetHealthCheck(name)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to get http health check %v", err)
|
||||
return nil, err
|
||||
}
|
||||
glog.V(2).Infof("ensureInternalHealthCheck: created health check %v", name)
|
||||
return hc, nil
|
||||
}
|
||||
|
||||
if healthChecksEqual(expectedHC, hc) {
|
||||
return hc, nil
|
||||
}
|
||||
|
||||
glog.V(2).Infof("ensureInternalHealthCheck: health check %v exists but parameters have drifted - updating...", name)
|
||||
if err := gce.UpdateHealthCheck(expectedHC); err != nil {
|
||||
glog.Warningf("Failed to reconcile http health check %v parameters", name)
|
||||
return nil, err
|
||||
}
|
||||
glog.V(2).Infof("ensureInternalHealthCheck: corrected health check %v parameters successful", name)
|
||||
return hc, nil
|
||||
}
|
||||
|
||||
func (gce *GCECloud) ensureInternalInstanceGroup(name, zone string, nodes []*v1.Node) (string, error) {
|
||||
glog.V(2).Infof("ensureInternalInstanceGroup(%v, %v): checking group that it contains %v nodes", name, zone, len(nodes))
|
||||
ig, err := gce.GetInstanceGroup(name, zone)
|
||||
if err != nil && !isNotFound(err) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
kubeNodes := sets.NewString()
|
||||
for _, n := range nodes {
|
||||
kubeNodes.Insert(n.Name)
|
||||
}
|
||||
|
||||
gceNodes := sets.NewString()
|
||||
if ig == nil {
|
||||
glog.V(2).Infof("ensureInternalInstanceGroup(%v, %v): creating instance group", name, zone)
|
||||
newIG := &compute.InstanceGroup{Name: name}
|
||||
if err = gce.CreateInstanceGroup(newIG, zone); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
ig, err = gce.GetInstanceGroup(name, zone)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
} else {
|
||||
instances, err := gce.ListInstancesInInstanceGroup(name, zone, allInstances)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, ins := range instances.Items {
|
||||
parts := strings.Split(ins.Instance, "/")
|
||||
gceNodes.Insert(parts[len(parts)-1])
|
||||
}
|
||||
}
|
||||
|
||||
removeNodes := gceNodes.Difference(kubeNodes).List()
|
||||
addNodes := kubeNodes.Difference(gceNodes).List()
|
||||
|
||||
if len(removeNodes) != 0 {
|
||||
glog.V(2).Infof("ensureInternalInstanceGroup(%v, %v): removing nodes: %v", name, zone, removeNodes)
|
||||
instanceRefs := gce.ToInstanceReferences(zone, removeNodes)
|
||||
// Possible we'll receive 404's here if the instance was deleted before getting to this point.
|
||||
if err = gce.RemoveInstancesFromInstanceGroup(name, zone, instanceRefs); err != nil && !isNotFound(err) {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
if len(addNodes) != 0 {
|
||||
glog.V(2).Infof("ensureInternalInstanceGroup(%v, %v): adding nodes: %v", name, zone, addNodes)
|
||||
instanceRefs := gce.ToInstanceReferences(zone, addNodes)
|
||||
if err = gce.AddInstancesToInstanceGroup(name, zone, instanceRefs); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
return ig.SelfLink, nil
|
||||
}
|
||||
|
||||
// ensureInternalInstanceGroups generates an unmanaged instance group for every zone
|
||||
// where a K8s node exists. It also ensures that each node belongs to an instance group
|
||||
func (gce *GCECloud) ensureInternalInstanceGroups(name string, nodes []*v1.Node) ([]string, error) {
|
||||
zonedNodes := splitNodesByZone(nodes)
|
||||
glog.V(2).Infof("ensureInternalInstanceGroups(%v): %d nodes over %d zones in region %v", name, len(nodes), len(zonedNodes), gce.region)
|
||||
var igLinks []string
|
||||
for zone, nodes := range zonedNodes {
|
||||
igLink, err := gce.ensureInternalInstanceGroup(name, zone, nodes)
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
igLinks = append(igLinks, igLink)
|
||||
}
|
||||
|
||||
return igLinks, nil
|
||||
}
|
||||
|
||||
func (gce *GCECloud) ensureInternalInstanceGroupsDeleted(name string) error {
|
||||
// List of nodes isn't available here - fetch all zones in region and try deleting this cluster's ig
|
||||
zones, err := gce.ListZonesInRegion(gce.region)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
glog.V(2).Infof("ensureInternalInstanceGroupsDeleted(%v): attempting delete instance group in all %d zones", name, len(zones))
|
||||
for _, z := range zones {
|
||||
if err := gce.DeleteInstanceGroup(name, z.Name); err != nil && !isNotFoundOrInUse(err) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gce *GCECloud) ensureInternalBackendService(name, description string, affinityType v1.ServiceAffinity, scheme lbScheme, protocol v1.Protocol, igLinks []string, hcLink string) error {
|
||||
glog.V(2).Infof("ensureInternalBackendService(%v, %v, %v): checking existing backend service with %d groups", name, scheme, protocol, len(igLinks))
|
||||
bs, err := gce.GetRegionBackendService(name, gce.region)
|
||||
if err != nil && !isNotFound(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
backends := backendsFromGroupLinks(igLinks)
|
||||
expectedBS := &compute.BackendService{
|
||||
Name: name,
|
||||
Protocol: string(protocol),
|
||||
Description: description,
|
||||
HealthChecks: []string{hcLink},
|
||||
Backends: backends,
|
||||
SessionAffinity: translateAffinityType(affinityType),
|
||||
LoadBalancingScheme: string(scheme),
|
||||
}
|
||||
|
||||
// Create backend service if none was found
|
||||
if bs == nil {
|
||||
glog.V(2).Infof("ensureInternalBackendService: creating backend service %v", name)
|
||||
err := gce.CreateRegionBackendService(expectedBS, gce.region)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
glog.V(2).Infof("ensureInternalBackendService: created backend service %v successfully", name)
|
||||
return nil
|
||||
}
|
||||
// Check existing backend service
|
||||
existingIGLinks := sets.NewString()
|
||||
for _, be := range bs.Backends {
|
||||
existingIGLinks.Insert(be.Group)
|
||||
}
|
||||
|
||||
if backendSvcEqual(expectedBS, bs) {
|
||||
return nil
|
||||
}
|
||||
|
||||
glog.V(2).Infof("ensureInternalBackendService: updating backend service %v", name)
|
||||
// Set fingerprint for optimistic locking
|
||||
expectedBS.Fingerprint = bs.Fingerprint
|
||||
if err := gce.UpdateRegionBackendService(expectedBS, gce.region); err != nil {
|
||||
return err
|
||||
}
|
||||
glog.V(2).Infof("ensureInternalBackendService: updated backend service %v successfully", name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ensureInternalBackendServiceGroups updates backend services if their list of backend instance groups is incorrect.
|
||||
func (gce *GCECloud) ensureInternalBackendServiceGroups(name string, igLinks []string) error {
|
||||
glog.V(2).Infof("ensureInternalBackendServiceGroups(%v): checking existing backend service's groups", name)
|
||||
bs, err := gce.GetRegionBackendService(name, gce.region)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
backends := backendsFromGroupLinks(igLinks)
|
||||
if backendsListEqual(bs.Backends, backends) {
|
||||
return nil
|
||||
}
|
||||
|
||||
glog.V(2).Infof("ensureInternalBackendServiceGroups: updating backend service %v", name)
|
||||
if err := gce.UpdateRegionBackendService(bs, gce.region); err != nil {
|
||||
return err
|
||||
}
|
||||
glog.V(2).Infof("ensureInternalBackendServiceGroups: updated backend service %v successfully", name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func shareBackendService(svc *v1.Service) bool {
|
||||
return GetLoadBalancerAnnotationBackendShare(svc) && !v1_service.RequestsOnlyLocalTraffic(svc)
|
||||
}
|
||||
|
||||
func backendsFromGroupLinks(igLinks []string) []*compute.Backend {
|
||||
var backends []*compute.Backend
|
||||
for _, igLink := range igLinks {
|
||||
backends = append(backends, &compute.Backend{
|
||||
Group: igLink,
|
||||
})
|
||||
}
|
||||
return backends
|
||||
}
|
||||
|
||||
func newInternalLBHealthCheck(name string, svcName types.NamespacedName, shared bool, path string, port int32) *compute.HealthCheck {
|
||||
httpSettings := compute.HTTPHealthCheck{
|
||||
Port: int64(port),
|
||||
RequestPath: path,
|
||||
}
|
||||
desc := ""
|
||||
if !shared {
|
||||
desc = makeHealthCheckDescription(svcName.String())
|
||||
}
|
||||
return &compute.HealthCheck{
|
||||
Name: name,
|
||||
CheckIntervalSec: gceHcCheckIntervalSeconds,
|
||||
TimeoutSec: gceHcTimeoutSeconds,
|
||||
HealthyThreshold: gceHcHealthyThreshold,
|
||||
UnhealthyThreshold: gceHcUnhealthyThreshold,
|
||||
HttpHealthCheck: &httpSettings,
|
||||
Type: "HTTP",
|
||||
Description: desc,
|
||||
}
|
||||
}
|
||||
|
||||
func firewallRuleEqual(a, b *compute.Firewall) bool {
|
||||
return a.Description == b.Description &&
|
||||
len(a.Allowed) == 1 && len(a.Allowed) == len(b.Allowed) &&
|
||||
a.Allowed[0].IPProtocol == b.Allowed[0].IPProtocol &&
|
||||
equalStringSets(a.Allowed[0].Ports, b.Allowed[0].Ports) &&
|
||||
equalStringSets(a.SourceRanges, b.SourceRanges) &&
|
||||
equalStringSets(a.TargetTags, b.TargetTags)
|
||||
}
|
||||
|
||||
func healthChecksEqual(a, b *compute.HealthCheck) bool {
|
||||
return a.HttpHealthCheck != nil && b.HttpHealthCheck != nil &&
|
||||
a.HttpHealthCheck.Port == b.HttpHealthCheck.Port &&
|
||||
a.HttpHealthCheck.RequestPath == b.HttpHealthCheck.RequestPath &&
|
||||
a.Description == b.Description &&
|
||||
a.CheckIntervalSec == b.CheckIntervalSec &&
|
||||
a.TimeoutSec == b.TimeoutSec &&
|
||||
a.UnhealthyThreshold == b.UnhealthyThreshold &&
|
||||
a.HealthyThreshold == b.HealthyThreshold
|
||||
}
|
||||
|
||||
// backendsListEqual asserts that backend lists are equal by instance group link only
|
||||
func backendsListEqual(a, b []*compute.Backend) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
if len(a) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
aSet := sets.NewString()
|
||||
for _, v := range a {
|
||||
aSet.Insert(v.Group)
|
||||
}
|
||||
bSet := sets.NewString()
|
||||
for _, v := range b {
|
||||
bSet.Insert(v.Group)
|
||||
}
|
||||
|
||||
return aSet.Equal(bSet)
|
||||
}
|
||||
|
||||
func backendSvcEqual(a, b *compute.BackendService) bool {
|
||||
return a.Protocol == b.Protocol &&
|
||||
a.Description == b.Description &&
|
||||
a.SessionAffinity == b.SessionAffinity &&
|
||||
a.LoadBalancingScheme == b.LoadBalancingScheme &&
|
||||
equalStringSets(a.HealthChecks, b.HealthChecks) &&
|
||||
backendsListEqual(a.Backends, b.Backends)
|
||||
}
|
||||
|
||||
func fwdRuleEqual(a, b *compute.ForwardingRule) bool {
|
||||
return (a.IPAddress == "" || b.IPAddress == "" || a.IPAddress == b.IPAddress) &&
|
||||
a.IPProtocol == b.IPProtocol &&
|
||||
a.LoadBalancingScheme == b.LoadBalancingScheme &&
|
||||
equalStringSets(a.Ports, b.Ports) &&
|
||||
a.BackendService == b.BackendService
|
||||
}
|
||||
|
||||
func getPortsAndProtocol(svcPorts []v1.ServicePort) (ports []string, protocol v1.Protocol) {
|
||||
if len(svcPorts) == 0 {
|
||||
return []string{}, v1.ProtocolUDP
|
||||
}
|
||||
|
||||
// GCP doesn't support multiple protocols for a single load balancer
|
||||
protocol = svcPorts[0].Protocol
|
||||
for _, p := range svcPorts {
|
||||
ports = append(ports, strconv.Itoa(int(p.Port)))
|
||||
}
|
||||
return ports, protocol
|
||||
}
|
||||
|
||||
func (gce *GCECloud) getBackendServiceLink(name string) string {
|
||||
return gce.service.BasePath + strings.Join([]string{gce.projectID, "regions", gce.region, "backendServices", name}, "/")
|
||||
}
|
||||
|
||||
func getNameFromLink(link string) string {
|
||||
if link == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
fields := strings.Split(link, "/")
|
||||
return fields[len(fields)-1]
|
||||
}
|
||||
|
||||
func determineRequestedIP(svc *v1.Service, fwdRule *compute.ForwardingRule) string {
|
||||
if svc.Spec.LoadBalancerIP != "" {
|
||||
return svc.Spec.LoadBalancerIP
|
||||
}
|
||||
|
||||
if fwdRule != nil {
|
||||
return fwdRule.IPAddress
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
120
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_loadbalancer_naming.go
generated
vendored
Normal file
120
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_loadbalancer_naming.go
generated
vendored
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gce
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
// Internal Load Balancer
|
||||
|
||||
// Instance groups remain legacy named to stay consistent with ingress
|
||||
func makeInstanceGroupName(clusterID string) string {
|
||||
return fmt.Sprintf("k8s-ig--%s", clusterID)
|
||||
}
|
||||
|
||||
func makeBackendServiceName(loadBalancerName, clusterID string, shared bool, scheme lbScheme, protocol v1.Protocol, svcAffinity v1.ServiceAffinity) string {
|
||||
if shared {
|
||||
hash := sha1.New()
|
||||
|
||||
// For every non-nil option, hash its value. Currently, only service affinity is relevant.
|
||||
hash.Write([]byte(string(svcAffinity)))
|
||||
|
||||
hashed := hex.EncodeToString(hash.Sum(nil))
|
||||
hashed = hashed[:16]
|
||||
|
||||
// k8s- 4
|
||||
// {clusterid}- 17
|
||||
// {scheme}- 9 (internal/external)
|
||||
// {protocol}- 4 (tcp/udp)
|
||||
// nmv1- 5 (naming convention version)
|
||||
// {suffix} 16 (hash of settings)
|
||||
// -----------------
|
||||
// 55 characters used
|
||||
return fmt.Sprintf("k8s-%s-%s-%s-nmv1-%s", clusterID, strings.ToLower(string(scheme)), strings.ToLower(string(protocol)), hashed)
|
||||
}
|
||||
return loadBalancerName
|
||||
}
|
||||
|
||||
func makeHealthCheckName(loadBalancerName, clusterID string, shared bool) string {
|
||||
if shared {
|
||||
return fmt.Sprintf("k8s-%s-node", clusterID)
|
||||
}
|
||||
|
||||
return loadBalancerName
|
||||
}
|
||||
|
||||
func makeHealthCheckFirewallNameFromHC(healthCheckName string) string {
|
||||
return healthCheckName + "-hc"
|
||||
}
|
||||
|
||||
func makeHealthCheckFirewallName(loadBalancerName, clusterID string, shared bool) string {
|
||||
if shared {
|
||||
return fmt.Sprintf("k8s-%s-node-hc", clusterID)
|
||||
}
|
||||
return loadBalancerName + "-hc"
|
||||
}
|
||||
|
||||
func makeBackendServiceDescription(nm types.NamespacedName, shared bool) string {
|
||||
if shared {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf(`{"kubernetes.io/service-name":"%s"}`, nm.String())
|
||||
}
|
||||
|
||||
// External Load Balancer
|
||||
|
||||
// makeServiceDescription is used to generate descriptions for forwarding rules and addresses.
|
||||
func makeServiceDescription(serviceName string) string {
|
||||
return fmt.Sprintf(`{"kubernetes.io/service-name":"%s"}`, serviceName)
|
||||
}
|
||||
|
||||
// MakeNodesHealthCheckName returns name of the health check resource used by
|
||||
// the GCE load balancers (l4) for performing health checks on nodes.
|
||||
func MakeNodesHealthCheckName(clusterID string) string {
|
||||
return fmt.Sprintf("k8s-%v-node", clusterID)
|
||||
}
|
||||
|
||||
func makeHealthCheckDescription(serviceName string) string {
|
||||
return fmt.Sprintf(`{"kubernetes.io/service-name":"%s"}`, serviceName)
|
||||
}
|
||||
|
||||
// MakeHealthCheckFirewallName returns the firewall name used by the GCE load
|
||||
// balancers (l4) for performing health checks.
|
||||
func MakeHealthCheckFirewallName(clusterID, hcName string, isNodesHealthCheck bool) string {
|
||||
if isNodesHealthCheck {
|
||||
return MakeNodesHealthCheckName(clusterID) + "-http-hc"
|
||||
}
|
||||
return "k8s-" + hcName + "-http-hc"
|
||||
}
|
||||
|
||||
// MakeFirewallName returns the firewall name used by the GCE load
|
||||
// balancers (l4) for serving traffic.
|
||||
func MakeFirewallName(name string) string {
|
||||
return fmt.Sprintf("k8s-fw-%s", name)
|
||||
}
|
||||
|
||||
func makeFirewallDescription(serviceName, ipAddress string) string {
|
||||
return fmt.Sprintf(`{"kubernetes.io/service-name":"%s", "kubernetes.io/service-ip":"%s"}`,
|
||||
serviceName, ipAddress)
|
||||
}
|
148
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_networkendpointgroup.go
generated
vendored
Normal file
148
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_networkendpointgroup.go
generated
vendored
Normal file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gce
|
||||
|
||||
import (
|
||||
"context"
|
||||
computealpha "google.golang.org/api/compute/v0.alpha"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
NEGLoadBalancerType = "LOAD_BALANCING"
|
||||
NEGIPPortNetworkEndpointType = "GCE_VM_IP_PORT"
|
||||
)
|
||||
|
||||
func newNetworkEndpointGroupMetricContext(request string, zone string) *metricContext {
|
||||
return newGenericMetricContext("networkendpointgroup_", request, unusedMetricLabel, zone, computeAlphaVersion)
|
||||
}
|
||||
|
||||
func (gce *GCECloud) GetNetworkEndpointGroup(name string, zone string) (*computealpha.NetworkEndpointGroup, error) {
|
||||
if err := gce.alphaFeatureEnabled(AlphaFeatureNetworkEndpointGroup); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mc := newNetworkEndpointGroupMetricContext("get", zone)
|
||||
v, err := gce.serviceAlpha.NetworkEndpointGroups.Get(gce.ProjectID(), zone, name).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
||||
|
||||
func (gce *GCECloud) ListNetworkEndpointGroup(zone string) ([]*computealpha.NetworkEndpointGroup, error) {
|
||||
if err := gce.alphaFeatureEnabled(AlphaFeatureNetworkEndpointGroup); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mc := newNetworkEndpointGroupMetricContext("list", zone)
|
||||
networkEndpointGroups := []*computealpha.NetworkEndpointGroup{}
|
||||
err := gce.serviceAlpha.NetworkEndpointGroups.List(gce.ProjectID(), zone).Pages(context.Background(), func(res *computealpha.NetworkEndpointGroupList) error {
|
||||
networkEndpointGroups = append(networkEndpointGroups, res.Items...)
|
||||
return nil
|
||||
})
|
||||
return networkEndpointGroups, mc.Observe(err)
|
||||
}
|
||||
|
||||
func (gce *GCECloud) AggregatedListNetworkEndpointGroup() (map[string][]*computealpha.NetworkEndpointGroup, error) {
|
||||
if err := gce.alphaFeatureEnabled(AlphaFeatureNetworkEndpointGroup); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mc := newNetworkEndpointGroupMetricContext("aggregated_list", "")
|
||||
zoneNetworkEndpointGroupMap := map[string][]*computealpha.NetworkEndpointGroup{}
|
||||
err := gce.serviceAlpha.NetworkEndpointGroups.AggregatedList(gce.ProjectID()).Pages(context.Background(), func(res *computealpha.NetworkEndpointGroupAggregatedList) error {
|
||||
for key, negs := range res.Items {
|
||||
if len(negs.NetworkEndpointGroups) == 0 {
|
||||
continue
|
||||
}
|
||||
// key has the format of "zones/${zone_name}"
|
||||
zone := strings.Split(key, "/")[1]
|
||||
if _, ok := zoneNetworkEndpointGroupMap[zone]; !ok {
|
||||
zoneNetworkEndpointGroupMap[zone] = []*computealpha.NetworkEndpointGroup{}
|
||||
}
|
||||
zoneNetworkEndpointGroupMap[zone] = append(zoneNetworkEndpointGroupMap[zone], negs.NetworkEndpointGroups...)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return zoneNetworkEndpointGroupMap, mc.Observe(err)
|
||||
}
|
||||
|
||||
func (gce *GCECloud) CreateNetworkEndpointGroup(neg *computealpha.NetworkEndpointGroup, zone string) error {
|
||||
if err := gce.alphaFeatureEnabled(AlphaFeatureNetworkEndpointGroup); err != nil {
|
||||
return err
|
||||
}
|
||||
mc := newNetworkEndpointGroupMetricContext("create", zone)
|
||||
op, err := gce.serviceAlpha.NetworkEndpointGroups.Insert(gce.ProjectID(), zone, neg).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
return gce.waitForZoneOp(op, zone, mc)
|
||||
}
|
||||
|
||||
func (gce *GCECloud) DeleteNetworkEndpointGroup(name string, zone string) error {
|
||||
if err := gce.alphaFeatureEnabled(AlphaFeatureNetworkEndpointGroup); err != nil {
|
||||
return err
|
||||
}
|
||||
mc := newNetworkEndpointGroupMetricContext("delete", zone)
|
||||
op, err := gce.serviceAlpha.NetworkEndpointGroups.Delete(gce.ProjectID(), zone, name).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
return gce.waitForZoneOp(op, zone, mc)
|
||||
}
|
||||
|
||||
func (gce *GCECloud) AttachNetworkEndpoints(name, zone string, endpoints []*computealpha.NetworkEndpoint) error {
|
||||
if err := gce.alphaFeatureEnabled(AlphaFeatureNetworkEndpointGroup); err != nil {
|
||||
return err
|
||||
}
|
||||
mc := newNetworkEndpointGroupMetricContext("attach", zone)
|
||||
op, err := gce.serviceAlpha.NetworkEndpointGroups.AttachNetworkEndpoints(gce.ProjectID(), zone, name, &computealpha.NetworkEndpointGroupsAttachEndpointsRequest{
|
||||
NetworkEndpoints: endpoints,
|
||||
}).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
return gce.waitForZoneOp(op, zone, mc)
|
||||
}
|
||||
|
||||
func (gce *GCECloud) DetachNetworkEndpoints(name, zone string, endpoints []*computealpha.NetworkEndpoint) error {
|
||||
if err := gce.alphaFeatureEnabled(AlphaFeatureNetworkEndpointGroup); err != nil {
|
||||
return err
|
||||
}
|
||||
mc := newNetworkEndpointGroupMetricContext("detach", zone)
|
||||
op, err := gce.serviceAlpha.NetworkEndpointGroups.DetachNetworkEndpoints(gce.ProjectID(), zone, name, &computealpha.NetworkEndpointGroupsDetachEndpointsRequest{
|
||||
NetworkEndpoints: endpoints,
|
||||
}).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
return gce.waitForZoneOp(op, zone, mc)
|
||||
}
|
||||
|
||||
func (gce *GCECloud) ListNetworkEndpoints(name, zone string, showHealthStatus bool) ([]*computealpha.NetworkEndpointWithHealthStatus, error) {
|
||||
if err := gce.alphaFeatureEnabled(AlphaFeatureNetworkEndpointGroup); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
healthStatus := "SKIP"
|
||||
if showHealthStatus {
|
||||
healthStatus = "SHOW"
|
||||
}
|
||||
mc := newNetworkEndpointGroupMetricContext("list_networkendpoints", zone)
|
||||
networkEndpoints := []*computealpha.NetworkEndpointWithHealthStatus{}
|
||||
err := gce.serviceAlpha.NetworkEndpointGroups.ListNetworkEndpoints(gce.ProjectID(), zone, name, &computealpha.NetworkEndpointGroupsListEndpointsRequest{
|
||||
HealthStatus: healthStatus,
|
||||
}).Pages(context.Background(), func(res *computealpha.NetworkEndpointGroupsListNetworkEndpoints) error {
|
||||
networkEndpoints = append(networkEndpoints, res.Items...)
|
||||
return nil
|
||||
})
|
||||
return networkEndpoints, mc.Observe(err)
|
||||
}
|
180
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_op.go
generated
vendored
Normal file
180
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_op.go
generated
vendored
Normal file
@ -0,0 +1,180 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gce
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
|
||||
"github.com/golang/glog"
|
||||
computealpha "google.golang.org/api/compute/v0.alpha"
|
||||
computebeta "google.golang.org/api/compute/v0.beta"
|
||||
computev1 "google.golang.org/api/compute/v1"
|
||||
"google.golang.org/api/googleapi"
|
||||
)
|
||||
|
||||
func (gce *GCECloud) waitForOp(op *computev1.Operation, getOperation func(operationName string) (*computev1.Operation, error), mc *metricContext) error {
|
||||
if op == nil {
|
||||
return mc.Observe(fmt.Errorf("operation must not be nil"))
|
||||
}
|
||||
|
||||
if opIsDone(op) {
|
||||
return getErrorFromOp(op)
|
||||
}
|
||||
|
||||
opStart := time.Now()
|
||||
opName := op.Name
|
||||
|
||||
return wait.Poll(operationPollInterval, operationPollTimeoutDuration, func() (bool, error) {
|
||||
start := time.Now()
|
||||
gce.operationPollRateLimiter.Accept()
|
||||
duration := time.Now().Sub(start)
|
||||
if duration > 5*time.Second {
|
||||
glog.V(2).Infof("pollOperation: throttled %v for %v", duration, opName)
|
||||
}
|
||||
pollOp, err := getOperation(opName)
|
||||
if err != nil {
|
||||
glog.Warningf("GCE poll operation %s failed: pollOp: [%v] err: [%v] getErrorFromOp: [%v]",
|
||||
opName, pollOp, err, getErrorFromOp(pollOp))
|
||||
}
|
||||
|
||||
done := opIsDone(pollOp)
|
||||
if done {
|
||||
duration := time.Now().Sub(opStart)
|
||||
if duration > 1*time.Minute {
|
||||
// Log the JSON. It's cleaner than the %v structure.
|
||||
enc, err := pollOp.MarshalJSON()
|
||||
if err != nil {
|
||||
glog.Warningf("waitForOperation: long operation (%v): %v (failed to encode to JSON: %v)",
|
||||
duration, pollOp, err)
|
||||
} else {
|
||||
glog.V(2).Infof("waitForOperation: long operation (%v): %v",
|
||||
duration, string(enc))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return done, mc.Observe(getErrorFromOp(pollOp))
|
||||
})
|
||||
}
|
||||
|
||||
func opIsDone(op *computev1.Operation) bool {
|
||||
return op != nil && op.Status == "DONE"
|
||||
}
|
||||
|
||||
func getErrorFromOp(op *computev1.Operation) error {
|
||||
if op != nil && op.Error != nil && len(op.Error.Errors) > 0 {
|
||||
err := &googleapi.Error{
|
||||
Code: int(op.HttpErrorStatusCode),
|
||||
Message: op.Error.Errors[0].Message,
|
||||
}
|
||||
glog.Errorf("GCE operation failed: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gce *GCECloud) waitForGlobalOp(op gceObject, mc *metricContext) error {
|
||||
return gce.waitForGlobalOpInProject(op, gce.ProjectID(), mc)
|
||||
}
|
||||
|
||||
func (gce *GCECloud) waitForRegionOp(op gceObject, region string, mc *metricContext) error {
|
||||
return gce.waitForRegionOpInProject(op, gce.ProjectID(), region, mc)
|
||||
}
|
||||
|
||||
func (gce *GCECloud) waitForZoneOp(op gceObject, zone string, mc *metricContext) error {
|
||||
return gce.waitForZoneOpInProject(op, gce.ProjectID(), zone, mc)
|
||||
}
|
||||
|
||||
func (gce *GCECloud) waitForGlobalOpInProject(op gceObject, projectID string, mc *metricContext) error {
|
||||
switch v := op.(type) {
|
||||
case *computealpha.Operation:
|
||||
return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) {
|
||||
op, err := gce.serviceAlpha.GlobalOperations.Get(projectID, operationName).Do()
|
||||
return convertToV1Operation(op), err
|
||||
}, mc)
|
||||
case *computebeta.Operation:
|
||||
return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) {
|
||||
op, err := gce.serviceBeta.GlobalOperations.Get(projectID, operationName).Do()
|
||||
return convertToV1Operation(op), err
|
||||
}, mc)
|
||||
case *computev1.Operation:
|
||||
return gce.waitForOp(op.(*computev1.Operation), func(operationName string) (*computev1.Operation, error) {
|
||||
return gce.service.GlobalOperations.Get(projectID, operationName).Do()
|
||||
}, mc)
|
||||
default:
|
||||
return fmt.Errorf("unexpected type: %T", v)
|
||||
}
|
||||
}
|
||||
|
||||
func (gce *GCECloud) waitForRegionOpInProject(op gceObject, projectID, region string, mc *metricContext) error {
|
||||
switch v := op.(type) {
|
||||
case *computealpha.Operation:
|
||||
return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) {
|
||||
op, err := gce.serviceAlpha.RegionOperations.Get(projectID, region, operationName).Do()
|
||||
return convertToV1Operation(op), err
|
||||
}, mc)
|
||||
case *computebeta.Operation:
|
||||
return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) {
|
||||
op, err := gce.serviceBeta.RegionOperations.Get(projectID, region, operationName).Do()
|
||||
return convertToV1Operation(op), err
|
||||
}, mc)
|
||||
case *computev1.Operation:
|
||||
return gce.waitForOp(op.(*computev1.Operation), func(operationName string) (*computev1.Operation, error) {
|
||||
return gce.service.RegionOperations.Get(projectID, region, operationName).Do()
|
||||
}, mc)
|
||||
default:
|
||||
return fmt.Errorf("unexpected type: %T", v)
|
||||
}
|
||||
}
|
||||
|
||||
func (gce *GCECloud) waitForZoneOpInProject(op gceObject, projectID, zone string, mc *metricContext) error {
|
||||
switch v := op.(type) {
|
||||
case *computealpha.Operation:
|
||||
return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) {
|
||||
op, err := gce.serviceAlpha.ZoneOperations.Get(projectID, zone, operationName).Do()
|
||||
return convertToV1Operation(op), err
|
||||
}, mc)
|
||||
case *computebeta.Operation:
|
||||
return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) {
|
||||
op, err := gce.serviceBeta.ZoneOperations.Get(projectID, zone, operationName).Do()
|
||||
return convertToV1Operation(op), err
|
||||
}, mc)
|
||||
case *computev1.Operation:
|
||||
return gce.waitForOp(op.(*computev1.Operation), func(operationName string) (*computev1.Operation, error) {
|
||||
return gce.service.ZoneOperations.Get(projectID, zone, operationName).Do()
|
||||
}, mc)
|
||||
default:
|
||||
return fmt.Errorf("unexpected type: %T", v)
|
||||
}
|
||||
}
|
||||
|
||||
func convertToV1Operation(object gceObject) *computev1.Operation {
|
||||
enc, err := object.MarshalJSON()
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Failed to encode to json: %v", err))
|
||||
}
|
||||
var op computev1.Operation
|
||||
if err := json.Unmarshal(enc, &op); err != nil {
|
||||
panic(fmt.Sprintf("Failed to convert GCE apiObject %v to v1 operation: %v", object, err))
|
||||
}
|
||||
return &op
|
||||
}
|
116
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_routes.go
generated
vendored
Normal file
116
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_routes.go
generated
vendored
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gce
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path"
|
||||
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/kubernetes/pkg/cloudprovider"
|
||||
|
||||
"github.com/golang/glog"
|
||||
compute "google.golang.org/api/compute/v1"
|
||||
)
|
||||
|
||||
func newRoutesMetricContext(request string) *metricContext {
|
||||
return newGenericMetricContext("routes", request, unusedMetricLabel, unusedMetricLabel, computeV1Version)
|
||||
}
|
||||
|
||||
func (gce *GCECloud) ListRoutes(clusterName string) ([]*cloudprovider.Route, error) {
|
||||
var routes []*cloudprovider.Route
|
||||
pageToken := ""
|
||||
page := 0
|
||||
for ; page == 0 || (pageToken != "" && page < maxPages); page++ {
|
||||
mc := newRoutesMetricContext("list_page")
|
||||
listCall := gce.service.Routes.List(gce.NetworkProjectID())
|
||||
|
||||
prefix := truncateClusterName(clusterName)
|
||||
// Filter for routes starting with clustername AND belonging to the
|
||||
// relevant gcp network AND having description = "k8s-node-route".
|
||||
filter := "(name eq " + prefix + "-.*) "
|
||||
filter = filter + "(network eq " + gce.NetworkURL() + ") "
|
||||
filter = filter + "(description eq " + k8sNodeRouteTag + ")"
|
||||
listCall = listCall.Filter(filter)
|
||||
if pageToken != "" {
|
||||
listCall = listCall.PageToken(pageToken)
|
||||
}
|
||||
res, err := listCall.Do()
|
||||
mc.Observe(err)
|
||||
if err != nil {
|
||||
glog.Errorf("Error getting routes from GCE: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
pageToken = res.NextPageToken
|
||||
for _, r := range res.Items {
|
||||
target := path.Base(r.NextHopInstance)
|
||||
// TODO: Should we lastComponent(target) this?
|
||||
targetNodeName := types.NodeName(target) // NodeName == Instance Name on GCE
|
||||
routes = append(routes, &cloudprovider.Route{Name: r.Name, TargetNode: targetNodeName, DestinationCIDR: r.DestRange})
|
||||
}
|
||||
}
|
||||
if page >= maxPages {
|
||||
glog.Errorf("ListRoutes exceeded maxPages=%d for Routes.List; truncating.", maxPages)
|
||||
}
|
||||
return routes, nil
|
||||
}
|
||||
|
||||
func (gce *GCECloud) CreateRoute(clusterName string, nameHint string, route *cloudprovider.Route) error {
|
||||
routeName := truncateClusterName(clusterName) + "-" + nameHint
|
||||
|
||||
instanceName := mapNodeNameToInstanceName(route.TargetNode)
|
||||
targetInstance, err := gce.getInstanceByName(instanceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mc := newRoutesMetricContext("create")
|
||||
insertOp, err := gce.service.Routes.Insert(gce.NetworkProjectID(), &compute.Route{
|
||||
Name: routeName,
|
||||
DestRange: route.DestinationCIDR,
|
||||
NextHopInstance: fmt.Sprintf("zones/%s/instances/%s", targetInstance.Zone, targetInstance.Name),
|
||||
Network: gce.NetworkURL(),
|
||||
Priority: 1000,
|
||||
Description: k8sNodeRouteTag,
|
||||
}).Do()
|
||||
if err != nil {
|
||||
if isHTTPErrorCode(err, http.StatusConflict) {
|
||||
glog.Infof("Route %v already exists.", routeName)
|
||||
return nil
|
||||
} else {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
}
|
||||
return gce.waitForGlobalOpInProject(insertOp, gce.NetworkProjectID(), mc)
|
||||
}
|
||||
|
||||
func (gce *GCECloud) DeleteRoute(clusterName string, route *cloudprovider.Route) error {
|
||||
mc := newRoutesMetricContext("delete")
|
||||
deleteOp, err := gce.service.Routes.Delete(gce.NetworkProjectID(), route.Name).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
return gce.waitForGlobalOpInProject(deleteOp, gce.NetworkProjectID(), mc)
|
||||
}
|
||||
|
||||
func truncateClusterName(clusterName string) string {
|
||||
if len(clusterName) > 26 {
|
||||
return clusterName[:26]
|
||||
}
|
||||
return clusterName
|
||||
}
|
73
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_targetpool.go
generated
vendored
Normal file
73
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_targetpool.go
generated
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gce
|
||||
|
||||
import compute "google.golang.org/api/compute/v1"
|
||||
|
||||
func newTargetPoolMetricContext(request, region string) *metricContext {
|
||||
return newGenericMetricContext("targetpool", request, region, unusedMetricLabel, computeV1Version)
|
||||
}
|
||||
|
||||
// GetTargetPool returns the TargetPool by name.
|
||||
func (gce *GCECloud) GetTargetPool(name, region string) (*compute.TargetPool, error) {
|
||||
mc := newTargetPoolMetricContext("get", region)
|
||||
v, err := gce.service.TargetPools.Get(gce.projectID, region, name).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
||||
|
||||
// CreateTargetPool creates the passed TargetPool
|
||||
func (gce *GCECloud) CreateTargetPool(tp *compute.TargetPool, region string) error {
|
||||
mc := newTargetPoolMetricContext("create", region)
|
||||
op, err := gce.service.TargetPools.Insert(gce.projectID, region, tp).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
|
||||
return gce.waitForRegionOp(op, region, mc)
|
||||
}
|
||||
|
||||
// DeleteTargetPool deletes TargetPool by name.
|
||||
func (gce *GCECloud) DeleteTargetPool(name, region string) error {
|
||||
mc := newTargetPoolMetricContext("delete", region)
|
||||
op, err := gce.service.TargetPools.Delete(gce.projectID, region, name).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
return gce.waitForRegionOp(op, region, mc)
|
||||
}
|
||||
|
||||
// AddInstancesToTargetPool adds instances by link to the TargetPool
|
||||
func (gce *GCECloud) AddInstancesToTargetPool(name, region string, instanceRefs []*compute.InstanceReference) error {
|
||||
add := &compute.TargetPoolsAddInstanceRequest{Instances: instanceRefs}
|
||||
mc := newTargetPoolMetricContext("add_instances", region)
|
||||
op, err := gce.service.TargetPools.AddInstance(gce.projectID, region, name, add).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
return gce.waitForRegionOp(op, region, mc)
|
||||
}
|
||||
|
||||
// RemoveInstancesToTargetPool removes instances by link to the TargetPool
|
||||
func (gce *GCECloud) RemoveInstancesFromTargetPool(name, region string, instanceRefs []*compute.InstanceReference) error {
|
||||
remove := &compute.TargetPoolsRemoveInstanceRequest{Instances: instanceRefs}
|
||||
mc := newTargetPoolMetricContext("remove_instances", region)
|
||||
op, err := gce.service.TargetPools.RemoveInstance(gce.projectID, region, name, remove).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
return gce.waitForRegionOp(op, region, mc)
|
||||
}
|
138
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_targetproxy.go
generated
vendored
Normal file
138
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_targetproxy.go
generated
vendored
Normal file
@ -0,0 +1,138 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gce
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
compute "google.golang.org/api/compute/v1"
|
||||
)
|
||||
|
||||
func newTargetProxyMetricContext(request string) *metricContext {
|
||||
return newGenericMetricContext("targetproxy", request, unusedMetricLabel, unusedMetricLabel, computeV1Version)
|
||||
}
|
||||
|
||||
// GetTargetHttpProxy returns the UrlMap by name.
|
||||
func (gce *GCECloud) GetTargetHttpProxy(name string) (*compute.TargetHttpProxy, error) {
|
||||
mc := newTargetProxyMetricContext("get")
|
||||
v, err := gce.service.TargetHttpProxies.Get(gce.projectID, name).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
||||
|
||||
// CreateTargetHttpProxy creates a TargetHttpProxy
|
||||
func (gce *GCECloud) CreateTargetHttpProxy(proxy *compute.TargetHttpProxy) error {
|
||||
mc := newTargetProxyMetricContext("create")
|
||||
op, err := gce.service.TargetHttpProxies.Insert(gce.projectID, proxy).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
return gce.waitForGlobalOp(op, mc)
|
||||
}
|
||||
|
||||
// SetUrlMapForTargetHttpProxy sets the given UrlMap for the given TargetHttpProxy.
|
||||
func (gce *GCECloud) SetUrlMapForTargetHttpProxy(proxy *compute.TargetHttpProxy, urlMap *compute.UrlMap) error {
|
||||
mc := newTargetProxyMetricContext("set_url_map")
|
||||
op, err := gce.service.TargetHttpProxies.SetUrlMap(
|
||||
gce.projectID, proxy.Name, &compute.UrlMapReference{UrlMap: urlMap.SelfLink}).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
return gce.waitForGlobalOp(op, mc)
|
||||
}
|
||||
|
||||
// DeleteTargetHttpProxy deletes the TargetHttpProxy by name.
|
||||
func (gce *GCECloud) DeleteTargetHttpProxy(name string) error {
|
||||
mc := newTargetProxyMetricContext("delete")
|
||||
op, err := gce.service.TargetHttpProxies.Delete(gce.projectID, name).Do()
|
||||
if err != nil {
|
||||
if isHTTPErrorCode(err, http.StatusNotFound) {
|
||||
return nil
|
||||
}
|
||||
return mc.Observe(err)
|
||||
}
|
||||
return gce.waitForGlobalOp(op, mc)
|
||||
}
|
||||
|
||||
// ListTargetHttpProxies lists all TargetHttpProxies in the project.
|
||||
func (gce *GCECloud) ListTargetHttpProxies() (*compute.TargetHttpProxyList, error) {
|
||||
mc := newTargetProxyMetricContext("list")
|
||||
// TODO: use PageToken to list all not just the first 500
|
||||
v, err := gce.service.TargetHttpProxies.List(gce.projectID).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
||||
|
||||
// TargetHttpsProxy management
|
||||
|
||||
// GetTargetHttpsProxy returns the UrlMap by name.
|
||||
func (gce *GCECloud) GetTargetHttpsProxy(name string) (*compute.TargetHttpsProxy, error) {
|
||||
mc := newTargetProxyMetricContext("get")
|
||||
v, err := gce.service.TargetHttpsProxies.Get(gce.projectID, name).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
||||
|
||||
// CreateTargetHttpsProxy creates a TargetHttpsProxy
|
||||
func (gce *GCECloud) CreateTargetHttpsProxy(proxy *compute.TargetHttpsProxy) error {
|
||||
mc := newTargetProxyMetricContext("create")
|
||||
op, err := gce.service.TargetHttpsProxies.Insert(gce.projectID, proxy).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
return gce.waitForGlobalOp(op, mc)
|
||||
}
|
||||
|
||||
// SetUrlMapForTargetHttpsProxy sets the given UrlMap for the given TargetHttpsProxy.
|
||||
func (gce *GCECloud) SetUrlMapForTargetHttpsProxy(proxy *compute.TargetHttpsProxy, urlMap *compute.UrlMap) error {
|
||||
mc := newTargetProxyMetricContext("set_url_map")
|
||||
op, err := gce.service.TargetHttpsProxies.SetUrlMap(
|
||||
gce.projectID, proxy.Name, &compute.UrlMapReference{UrlMap: urlMap.SelfLink}).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
return gce.waitForGlobalOp(op, mc)
|
||||
}
|
||||
|
||||
// SetSslCertificateForTargetHttpsProxy sets the given SslCertificate for the given TargetHttpsProxy.
|
||||
func (gce *GCECloud) SetSslCertificateForTargetHttpsProxy(proxy *compute.TargetHttpsProxy, sslCert *compute.SslCertificate) error {
|
||||
mc := newTargetProxyMetricContext("set_ssl_cert")
|
||||
op, err := gce.service.TargetHttpsProxies.SetSslCertificates(
|
||||
gce.projectID, proxy.Name, &compute.TargetHttpsProxiesSetSslCertificatesRequest{SslCertificates: []string{sslCert.SelfLink}}).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
return gce.waitForGlobalOp(op, mc)
|
||||
}
|
||||
|
||||
// DeleteTargetHttpsProxy deletes the TargetHttpsProxy by name.
|
||||
func (gce *GCECloud) DeleteTargetHttpsProxy(name string) error {
|
||||
mc := newTargetProxyMetricContext("delete")
|
||||
op, err := gce.service.TargetHttpsProxies.Delete(gce.projectID, name).Do()
|
||||
if err != nil {
|
||||
if isHTTPErrorCode(err, http.StatusNotFound) {
|
||||
return nil
|
||||
}
|
||||
return mc.Observe(err)
|
||||
}
|
||||
return gce.waitForGlobalOp(op, mc)
|
||||
}
|
||||
|
||||
// ListTargetHttpsProxies lists all TargetHttpsProxies in the project.
|
||||
func (gce *GCECloud) ListTargetHttpsProxies() (*compute.TargetHttpsProxyList, error) {
|
||||
mc := newTargetProxyMetricContext("list")
|
||||
// TODO: use PageToken to list all not just the first 500
|
||||
v, err := gce.service.TargetHttpsProxies.List(gce.projectID).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
688
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_test.go
generated
vendored
Normal file
688
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_test.go
generated
vendored
Normal file
@ -0,0 +1,688 @@
|
||||
/*
|
||||
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 gce
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/oauth2/google"
|
||||
|
||||
computealpha "google.golang.org/api/compute/v0.alpha"
|
||||
computebeta "google.golang.org/api/compute/v0.beta"
|
||||
computev1 "google.golang.org/api/compute/v1"
|
||||
"k8s.io/kubernetes/pkg/cloudprovider"
|
||||
)
|
||||
|
||||
func TestReadConfigFile(t *testing.T) {
|
||||
const s = `[Global]
|
||||
token-url = my-token-url
|
||||
token-body = my-token-body
|
||||
project-id = my-project
|
||||
network-project-id = my-network-project
|
||||
network-name = my-network
|
||||
subnetwork-name = my-subnetwork
|
||||
secondary-range-name = my-secondary-range
|
||||
node-tags = my-node-tag1
|
||||
node-instance-prefix = my-prefix
|
||||
multizone = true
|
||||
`
|
||||
reader := strings.NewReader(s)
|
||||
config, err := readConfig(reader)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected config parsing error %v", err)
|
||||
}
|
||||
|
||||
expected := &ConfigFile{Global: ConfigGlobal{
|
||||
TokenURL: "my-token-url",
|
||||
TokenBody: "my-token-body",
|
||||
ProjectID: "my-project",
|
||||
NetworkProjectID: "my-network-project",
|
||||
NetworkName: "my-network",
|
||||
SubnetworkName: "my-subnetwork",
|
||||
SecondaryRangeName: "my-secondary-range",
|
||||
NodeTags: []string{"my-node-tag1"},
|
||||
NodeInstancePrefix: "my-prefix",
|
||||
Multizone: true,
|
||||
}}
|
||||
|
||||
if !reflect.DeepEqual(expected, config) {
|
||||
t.Fatalf("Expected config file values to be read into ConfigFile struct. \nExpected:\n%+v\nActual:\n%+v", expected, config)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtraKeyInConfig(t *testing.T) {
|
||||
const s = `[Global]
|
||||
project-id = my-project
|
||||
unknown-key = abc
|
||||
network-name = my-network
|
||||
`
|
||||
reader := strings.NewReader(s)
|
||||
config, err := readConfig(reader)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected config parsing error %v", err)
|
||||
}
|
||||
if config.Global.ProjectID != "my-project" || config.Global.NetworkName != "my-network" {
|
||||
t.Fatalf("Expected config values to continue to be read despite extra key-value pair.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRegion(t *testing.T) {
|
||||
zoneName := "us-central1-b"
|
||||
regionName, err := GetGCERegion(zoneName)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error from GetGCERegion: %v", err)
|
||||
}
|
||||
if regionName != "us-central1" {
|
||||
t.Errorf("Unexpected region from GetGCERegion: %s", regionName)
|
||||
}
|
||||
gce := &GCECloud{
|
||||
localZone: zoneName,
|
||||
region: regionName,
|
||||
}
|
||||
zones, ok := gce.Zones()
|
||||
if !ok {
|
||||
t.Fatalf("Unexpected missing zones impl")
|
||||
}
|
||||
zone, err := zones.GetZone()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
if zone.Region != "us-central1" {
|
||||
t.Errorf("Unexpected region: %s", zone.Region)
|
||||
}
|
||||
}
|
||||
|
||||
func TestComparingHostURLs(t *testing.T) {
|
||||
tests := []struct {
|
||||
host1 string
|
||||
zone string
|
||||
name string
|
||||
expectEqual bool
|
||||
}{
|
||||
{
|
||||
host1: "https://www.googleapis.com/compute/v1/projects/1234567/zones/us-central1-f/instances/kubernetes-node-fhx1",
|
||||
zone: "us-central1-f",
|
||||
name: "kubernetes-node-fhx1",
|
||||
expectEqual: true,
|
||||
},
|
||||
{
|
||||
host1: "https://www.googleapis.com/compute/v1/projects/cool-project/zones/us-central1-f/instances/kubernetes-node-fhx1",
|
||||
zone: "us-central1-f",
|
||||
name: "kubernetes-node-fhx1",
|
||||
expectEqual: true,
|
||||
},
|
||||
{
|
||||
host1: "https://www.googleapis.com/compute/v23/projects/1234567/zones/us-central1-f/instances/kubernetes-node-fhx1",
|
||||
zone: "us-central1-f",
|
||||
name: "kubernetes-node-fhx1",
|
||||
expectEqual: true,
|
||||
},
|
||||
{
|
||||
host1: "https://www.googleapis.com/compute/v24/projects/1234567/regions/us-central1/zones/us-central1-f/instances/kubernetes-node-fhx1",
|
||||
zone: "us-central1-f",
|
||||
name: "kubernetes-node-fhx1",
|
||||
expectEqual: true,
|
||||
},
|
||||
{
|
||||
host1: "https://www.googleapis.com/compute/v1/projects/1234567/zones/us-central1-f/instances/kubernetes-node-fhx1",
|
||||
zone: "us-central1-c",
|
||||
name: "kubernetes-node-fhx1",
|
||||
expectEqual: false,
|
||||
},
|
||||
{
|
||||
host1: "https://www.googleapis.com/compute/v1/projects/1234567/zones/us-central1-f/instances/kubernetes-node-fhx",
|
||||
zone: "us-central1-f",
|
||||
name: "kubernetes-node-fhx1",
|
||||
expectEqual: false,
|
||||
},
|
||||
{
|
||||
host1: "https://www.googleapis.com/compute/v1/projects/1234567/zones/us-central1-f/instances/kubernetes-node-fhx1",
|
||||
zone: "us-central1-f",
|
||||
name: "kubernetes-node-fhx",
|
||||
expectEqual: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
link1 := hostURLToComparablePath(test.host1)
|
||||
testInstance := &gceInstance{
|
||||
Name: canonicalizeInstanceName(test.name),
|
||||
Zone: test.zone,
|
||||
}
|
||||
link2 := testInstance.makeComparableHostPath()
|
||||
if test.expectEqual && link1 != link2 {
|
||||
t.Errorf("expected link1 and link2 to be equal, got %s and %s", link1, link2)
|
||||
} else if !test.expectEqual && link1 == link2 {
|
||||
t.Errorf("expected link1 and link2 not to be equal, got %s and %s", link1, link2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestScrubDNS(t *testing.T) {
|
||||
tcs := []struct {
|
||||
nameserversIn []string
|
||||
searchesIn []string
|
||||
nameserversOut []string
|
||||
searchesOut []string
|
||||
}{
|
||||
{
|
||||
nameserversIn: []string{"1.2.3.4", "5.6.7.8"},
|
||||
nameserversOut: []string{"1.2.3.4", "5.6.7.8"},
|
||||
},
|
||||
{
|
||||
searchesIn: []string{"c.prj.internal.", "12345678910.google.internal.", "google.internal."},
|
||||
searchesOut: []string{"c.prj.internal.", "google.internal."},
|
||||
},
|
||||
{
|
||||
searchesIn: []string{"c.prj.internal.", "12345678910.google.internal.", "zone.c.prj.internal.", "google.internal."},
|
||||
searchesOut: []string{"c.prj.internal.", "zone.c.prj.internal.", "google.internal."},
|
||||
},
|
||||
{
|
||||
searchesIn: []string{"c.prj.internal.", "12345678910.google.internal.", "zone.c.prj.internal.", "google.internal.", "unexpected"},
|
||||
searchesOut: []string{"c.prj.internal.", "zone.c.prj.internal.", "google.internal.", "unexpected"},
|
||||
},
|
||||
}
|
||||
gce := &GCECloud{}
|
||||
for i := range tcs {
|
||||
n, s := gce.ScrubDNS(tcs[i].nameserversIn, tcs[i].searchesIn)
|
||||
if !reflect.DeepEqual(n, tcs[i].nameserversOut) {
|
||||
t.Errorf("Expected %v, got %v", tcs[i].nameserversOut, n)
|
||||
}
|
||||
if !reflect.DeepEqual(s, tcs[i].searchesOut) {
|
||||
t.Errorf("Expected %v, got %v", tcs[i].searchesOut, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitProviderID(t *testing.T) {
|
||||
providers := []struct {
|
||||
providerID string
|
||||
|
||||
project string
|
||||
zone string
|
||||
instance string
|
||||
|
||||
fail bool
|
||||
}{
|
||||
{
|
||||
providerID: ProviderName + "://project-example-164317/us-central1-f/kubernetes-node-fhx1",
|
||||
project: "project-example-164317",
|
||||
zone: "us-central1-f",
|
||||
instance: "kubernetes-node-fhx1",
|
||||
fail: false,
|
||||
},
|
||||
{
|
||||
providerID: ProviderName + "://project-example.164317/us-central1-f/kubernetes-node-fhx1",
|
||||
project: "project-example.164317",
|
||||
zone: "us-central1-f",
|
||||
instance: "kubernetes-node-fhx1",
|
||||
fail: false,
|
||||
},
|
||||
{
|
||||
providerID: ProviderName + "://project-example-164317/us-central1-fkubernetes-node-fhx1",
|
||||
project: "",
|
||||
zone: "",
|
||||
instance: "",
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
providerID: ProviderName + ":/project-example-164317/us-central1-f/kubernetes-node-fhx1",
|
||||
project: "",
|
||||
zone: "",
|
||||
instance: "",
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
providerID: "aws://project-example-164317/us-central1-f/kubernetes-node-fhx1",
|
||||
project: "",
|
||||
zone: "",
|
||||
instance: "",
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
providerID: ProviderName + "://project-example-164317/us-central1-f/kubernetes-node-fhx1/",
|
||||
project: "",
|
||||
zone: "",
|
||||
instance: "",
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
providerID: ProviderName + "://project-example.164317//kubernetes-node-fhx1",
|
||||
project: "",
|
||||
zone: "",
|
||||
instance: "",
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
providerID: ProviderName + "://project-example.164317/kubernetes-node-fhx1",
|
||||
project: "",
|
||||
zone: "",
|
||||
instance: "",
|
||||
fail: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range providers {
|
||||
project, zone, instance, err := splitProviderID(test.providerID)
|
||||
if (err != nil) != test.fail {
|
||||
t.Errorf("Expected to fail=%t, with pattern %v", test.fail, test)
|
||||
}
|
||||
|
||||
if test.fail {
|
||||
continue
|
||||
}
|
||||
|
||||
if project != test.project {
|
||||
t.Errorf("Expected %v, but got %v", test.project, project)
|
||||
}
|
||||
if zone != test.zone {
|
||||
t.Errorf("Expected %v, but got %v", test.zone, zone)
|
||||
}
|
||||
if instance != test.instance {
|
||||
t.Errorf("Expected %v, but got %v", test.instance, instance)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetZoneByProviderID(t *testing.T) {
|
||||
tests := []struct {
|
||||
providerID string
|
||||
|
||||
expectedZone cloudprovider.Zone
|
||||
|
||||
fail bool
|
||||
description string
|
||||
}{
|
||||
{
|
||||
providerID: ProviderName + "://project-example-164317/us-central1-f/kubernetes-node-fhx1",
|
||||
expectedZone: cloudprovider.Zone{FailureDomain: "us-central1-f", Region: "us-central1"},
|
||||
fail: false,
|
||||
description: "standard gce providerID",
|
||||
},
|
||||
{
|
||||
providerID: ProviderName + "://project-example-164317/us-central1-f/kubernetes-node-fhx1/",
|
||||
expectedZone: cloudprovider.Zone{},
|
||||
fail: true,
|
||||
description: "too many slashes('/') trailing",
|
||||
},
|
||||
{
|
||||
providerID: ProviderName + "://project-example.164317//kubernetes-node-fhx1",
|
||||
expectedZone: cloudprovider.Zone{},
|
||||
fail: true,
|
||||
description: "too many slashes('/') embedded",
|
||||
},
|
||||
{
|
||||
providerID: ProviderName + "://project-example-164317/uscentral1f/kubernetes-node-fhx1",
|
||||
expectedZone: cloudprovider.Zone{},
|
||||
fail: true,
|
||||
description: "invalid name of the GCE zone",
|
||||
},
|
||||
}
|
||||
|
||||
gce := &GCECloud{
|
||||
localZone: "us-central1-f",
|
||||
region: "us-central1",
|
||||
}
|
||||
for _, test := range tests {
|
||||
zone, err := gce.GetZoneByProviderID(test.providerID)
|
||||
if (err != nil) != test.fail {
|
||||
t.Errorf("Expected to fail=%t, provider ID %v, tests %s", test.fail, test, test.description)
|
||||
}
|
||||
|
||||
if test.fail {
|
||||
continue
|
||||
}
|
||||
|
||||
if zone != test.expectedZone {
|
||||
t.Errorf("Expected %v, but got %v", test.expectedZone, zone)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateCloudConfigs(t *testing.T) {
|
||||
configBoilerplate := ConfigGlobal{
|
||||
TokenURL: "",
|
||||
TokenBody: "",
|
||||
ProjectID: "project-id",
|
||||
NetworkName: "network-name",
|
||||
SubnetworkName: "",
|
||||
SecondaryRangeName: "",
|
||||
NodeTags: []string{"node-tag"},
|
||||
NodeInstancePrefix: "node-prefix",
|
||||
Multizone: false,
|
||||
ApiEndpoint: "",
|
||||
LocalZone: "us-central1-a",
|
||||
AlphaFeatures: []string{},
|
||||
}
|
||||
|
||||
cloudBoilerplate := CloudConfig{
|
||||
ApiEndpoint: "",
|
||||
ProjectID: "project-id",
|
||||
NetworkProjectID: "",
|
||||
Region: "us-central1",
|
||||
Zone: "us-central1-a",
|
||||
ManagedZones: []string{"us-central1-a"},
|
||||
NetworkName: "network-name",
|
||||
SubnetworkName: "",
|
||||
NetworkURL: "",
|
||||
SubnetworkURL: "",
|
||||
SecondaryRangeName: "",
|
||||
NodeTags: []string{"node-tag"},
|
||||
TokenSource: google.ComputeTokenSource(""),
|
||||
NodeInstancePrefix: "node-prefix",
|
||||
UseMetadataServer: true,
|
||||
AlphaFeatureGate: &AlphaFeatureGate{map[string]bool{}},
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
config func() ConfigGlobal
|
||||
cloud func() CloudConfig
|
||||
}{
|
||||
{
|
||||
name: "Empty Config",
|
||||
config: func() ConfigGlobal { return configBoilerplate },
|
||||
cloud: func() CloudConfig { return cloudBoilerplate },
|
||||
},
|
||||
{
|
||||
name: "Nil token URL",
|
||||
config: func() ConfigGlobal {
|
||||
v := configBoilerplate
|
||||
v.TokenURL = "nil"
|
||||
return v
|
||||
},
|
||||
cloud: func() CloudConfig {
|
||||
v := cloudBoilerplate
|
||||
v.TokenSource = nil
|
||||
return v
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Network Project ID",
|
||||
config: func() ConfigGlobal {
|
||||
v := configBoilerplate
|
||||
v.NetworkProjectID = "my-awesome-project"
|
||||
return v
|
||||
},
|
||||
cloud: func() CloudConfig {
|
||||
v := cloudBoilerplate
|
||||
v.NetworkProjectID = "my-awesome-project"
|
||||
return v
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Specified API Endpint",
|
||||
config: func() ConfigGlobal {
|
||||
v := configBoilerplate
|
||||
v.ApiEndpoint = "https://www.googleapis.com/compute/staging_v1/"
|
||||
return v
|
||||
},
|
||||
cloud: func() CloudConfig {
|
||||
v := cloudBoilerplate
|
||||
v.ApiEndpoint = "https://www.googleapis.com/compute/staging_v1/"
|
||||
return v
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Network & Subnetwork names",
|
||||
config: func() ConfigGlobal {
|
||||
v := configBoilerplate
|
||||
v.NetworkName = "my-network"
|
||||
v.SubnetworkName = "my-subnetwork"
|
||||
return v
|
||||
},
|
||||
cloud: func() CloudConfig {
|
||||
v := cloudBoilerplate
|
||||
v.NetworkName = "my-network"
|
||||
v.SubnetworkName = "my-subnetwork"
|
||||
return v
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Network & Subnetwork URLs",
|
||||
config: func() ConfigGlobal {
|
||||
v := configBoilerplate
|
||||
v.NetworkName = "https://www.googleapis.com/compute/v1/projects/project-id/global/networks/my-network"
|
||||
v.SubnetworkName = "https://www.googleapis.com/compute/v1/projects/project-id/regions/us-central1/subnetworks/my-subnetwork"
|
||||
return v
|
||||
},
|
||||
cloud: func() CloudConfig {
|
||||
v := cloudBoilerplate
|
||||
v.NetworkName = ""
|
||||
v.SubnetworkName = ""
|
||||
v.NetworkURL = "https://www.googleapis.com/compute/v1/projects/project-id/global/networks/my-network"
|
||||
v.SubnetworkURL = "https://www.googleapis.com/compute/v1/projects/project-id/regions/us-central1/subnetworks/my-subnetwork"
|
||||
return v
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Multizone",
|
||||
config: func() ConfigGlobal {
|
||||
v := configBoilerplate
|
||||
v.Multizone = true
|
||||
return v
|
||||
},
|
||||
cloud: func() CloudConfig {
|
||||
v := cloudBoilerplate
|
||||
v.ManagedZones = nil
|
||||
return v
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Secondary Range Name",
|
||||
config: func() ConfigGlobal {
|
||||
v := configBoilerplate
|
||||
v.SecondaryRangeName = "my-secondary"
|
||||
return v
|
||||
},
|
||||
cloud: func() CloudConfig {
|
||||
v := cloudBoilerplate
|
||||
v.SecondaryRangeName = "my-secondary"
|
||||
return v
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
resultCloud, err := generateCloudConfig(&ConfigFile{Global: tc.config()})
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpect error: %v", err)
|
||||
}
|
||||
|
||||
v := tc.cloud()
|
||||
if !reflect.DeepEqual(*resultCloud, v) {
|
||||
t.Errorf("Got: \n%v\nWant\n%v\n", v, *resultCloud)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertToV1Operation(t *testing.T) {
|
||||
v1Op := getTestOperation()
|
||||
enc, _ := v1Op.MarshalJSON()
|
||||
var op interface{}
|
||||
var alphaOp computealpha.Operation
|
||||
var betaOp computebeta.Operation
|
||||
|
||||
if err := json.Unmarshal(enc, &alphaOp); err != nil {
|
||||
t.Errorf("Failed to unmarshal operation: %v", err)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(enc, &betaOp); err != nil {
|
||||
t.Errorf("Failed to unmarshal operation: %v", err)
|
||||
}
|
||||
|
||||
op = convertToV1Operation(&alphaOp)
|
||||
if _, ok := op.(*computev1.Operation); ok {
|
||||
if !reflect.DeepEqual(op, v1Op) {
|
||||
t.Errorf("Failed to maintain consistency across conversion")
|
||||
}
|
||||
} else {
|
||||
t.Errorf("Expect output to be type v1 operation, but got %v", op)
|
||||
}
|
||||
|
||||
op = convertToV1Operation(&betaOp)
|
||||
if _, ok := op.(*computev1.Operation); ok {
|
||||
if !reflect.DeepEqual(op, v1Op) {
|
||||
t.Errorf("Failed to maintain consistency across conversion")
|
||||
}
|
||||
} else {
|
||||
t.Errorf("Expect output to be type v1 operation, but got %v", op)
|
||||
}
|
||||
}
|
||||
|
||||
func getTestOperation() *computev1.Operation {
|
||||
return &computev1.Operation{
|
||||
Name: "test",
|
||||
Description: "test",
|
||||
Id: uint64(12345),
|
||||
Error: &computev1.OperationError{
|
||||
Errors: []*computev1.OperationErrorErrors{
|
||||
{
|
||||
Code: "555",
|
||||
Message: "error",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewAlphaFeatureGate(t *testing.T) {
|
||||
knownAlphaFeatures["foo"] = true
|
||||
knownAlphaFeatures["bar"] = true
|
||||
|
||||
testCases := []struct {
|
||||
alphaFeatures []string
|
||||
expectEnabled []string
|
||||
expectDisabled []string
|
||||
expectError bool
|
||||
}{
|
||||
// enable foo bar
|
||||
{
|
||||
alphaFeatures: []string{"foo", "bar"},
|
||||
expectEnabled: []string{"foo", "bar"},
|
||||
expectDisabled: []string{"aaa"},
|
||||
expectError: false,
|
||||
},
|
||||
// no alpha feature
|
||||
{
|
||||
alphaFeatures: []string{},
|
||||
expectEnabled: []string{},
|
||||
expectDisabled: []string{"foo", "bar"},
|
||||
expectError: false,
|
||||
},
|
||||
// unsupported alpha feature
|
||||
{
|
||||
alphaFeatures: []string{"aaa", "foo"},
|
||||
expectError: true,
|
||||
expectEnabled: []string{"foo"},
|
||||
expectDisabled: []string{"aaa"},
|
||||
},
|
||||
// enable foo
|
||||
{
|
||||
alphaFeatures: []string{"foo"},
|
||||
expectEnabled: []string{"foo"},
|
||||
expectDisabled: []string{"bar"},
|
||||
expectError: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
featureGate, err := NewAlphaFeatureGate(tc.alphaFeatures)
|
||||
|
||||
if (tc.expectError && err == nil) || (!tc.expectError && err != nil) {
|
||||
t.Errorf("Expect error to be %v, but got error %v", tc.expectError, err)
|
||||
}
|
||||
|
||||
for _, key := range tc.expectEnabled {
|
||||
if !featureGate.Enabled(key) {
|
||||
t.Errorf("Expect %q to be enabled.", key)
|
||||
}
|
||||
}
|
||||
for _, key := range tc.expectDisabled {
|
||||
if featureGate.Enabled(key) {
|
||||
t.Errorf("Expect %q to be disabled.", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
delete(knownAlphaFeatures, "foo")
|
||||
delete(knownAlphaFeatures, "bar")
|
||||
}
|
||||
|
||||
func TestGetRegionInURL(t *testing.T) {
|
||||
cases := map[string]string{
|
||||
"https://www.googleapis.com/compute/v1/projects/my-project/regions/us-central1/subnetworks/a": "us-central1",
|
||||
"https://www.googleapis.com/compute/v1/projects/my-project/regions/us-west2/subnetworks/b": "us-west2",
|
||||
"projects/my-project/regions/asia-central1/subnetworks/c": "asia-central1",
|
||||
"regions/europe-north2": "europe-north2",
|
||||
"my-url": "",
|
||||
"": "",
|
||||
}
|
||||
for input, output := range cases {
|
||||
result := getRegionInURL(input)
|
||||
if result != output {
|
||||
t.Errorf("Actual result %q does not match expected result %q for input: %q", result, output, input)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindSubnetForRegion(t *testing.T) {
|
||||
s := []string{
|
||||
"https://www.googleapis.com/compute/v1/projects/my-project/regions/us-central1/subnetworks/default-38b01f54907a15a7",
|
||||
"https://www.googleapis.com/compute/v1/projects/my-project/regions/us-west1/subnetworks/default",
|
||||
"https://www.googleapis.com/compute/v1/projects/my-project/regions/us-east1/subnetworks/default-277eec3815f742b6",
|
||||
"https://www.googleapis.com/compute/v1/projects/my-project/regions/us-east4/subnetworks/default",
|
||||
"https://www.googleapis.com/compute/v1/projects/my-project/regions/asia-northeast1/subnetworks/default",
|
||||
"https://www.googleapis.com/compute/v1/projects/my-project/regions/asia-east1/subnetworks/default-8e020b4b8b244809",
|
||||
"https://www.googleapis.com/compute/v1/projects/my-project/regions/australia-southeast1/subnetworks/default",
|
||||
"https://www.googleapis.com/compute/v1/projects/my-project/regions/southamerica-east1/subnetworks/default",
|
||||
"https://www.googleapis.com/compute/v1/projects/my-project/regions/europe-west3/subnetworks/default",
|
||||
"https://www.googleapis.com/compute/v1/projects/my-project/regions/asia-southeast1/subnetworks/default",
|
||||
"",
|
||||
}
|
||||
actual := findSubnetForRegion(s, "asia-east1")
|
||||
expectedResult := "https://www.googleapis.com/compute/v1/projects/my-project/regions/asia-east1/subnetworks/default-8e020b4b8b244809"
|
||||
if actual != expectedResult {
|
||||
t.Errorf("Actual result %q does not match expected result %q", actual, expectedResult)
|
||||
}
|
||||
|
||||
var nilSlice []string
|
||||
res := findSubnetForRegion(nilSlice, "us-central1")
|
||||
if res != "" {
|
||||
t.Errorf("expected an empty result, got %v", res)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLastComponent(t *testing.T) {
|
||||
cases := map[string]string{
|
||||
"https://www.googleapis.com/compute/v1/projects/my-project/regions/us-central1/subnetworks/a": "a",
|
||||
"https://www.googleapis.com/compute/v1/projects/my-project/regions/us-central1/subnetworks/b": "b",
|
||||
"projects/my-project/regions/us-central1/subnetworks/c": "c",
|
||||
"d": "d",
|
||||
"": "",
|
||||
}
|
||||
for input, output := range cases {
|
||||
result := lastComponent(input)
|
||||
if result != output {
|
||||
t.Errorf("Actual result %q does not match expected result %q for input: %q", result, output, input)
|
||||
}
|
||||
}
|
||||
}
|
75
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_urlmap.go
generated
vendored
Normal file
75
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_urlmap.go
generated
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gce
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
compute "google.golang.org/api/compute/v1"
|
||||
)
|
||||
|
||||
func newUrlMapMetricContext(request string) *metricContext {
|
||||
return newGenericMetricContext("urlmap", request, unusedMetricLabel, unusedMetricLabel, computeV1Version)
|
||||
}
|
||||
|
||||
// GetUrlMap returns the UrlMap by name.
|
||||
func (gce *GCECloud) GetUrlMap(name string) (*compute.UrlMap, error) {
|
||||
mc := newUrlMapMetricContext("get")
|
||||
v, err := gce.service.UrlMaps.Get(gce.projectID, name).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
||||
|
||||
// CreateUrlMap creates a url map
|
||||
func (gce *GCECloud) CreateUrlMap(urlMap *compute.UrlMap) error {
|
||||
mc := newUrlMapMetricContext("create")
|
||||
op, err := gce.service.UrlMaps.Insert(gce.projectID, urlMap).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
return gce.waitForGlobalOp(op, mc)
|
||||
}
|
||||
|
||||
// UpdateUrlMap applies the given UrlMap as an update
|
||||
func (gce *GCECloud) UpdateUrlMap(urlMap *compute.UrlMap) error {
|
||||
mc := newUrlMapMetricContext("update")
|
||||
op, err := gce.service.UrlMaps.Update(gce.projectID, urlMap.Name, urlMap).Do()
|
||||
if err != nil {
|
||||
return mc.Observe(err)
|
||||
}
|
||||
return gce.waitForGlobalOp(op, mc)
|
||||
}
|
||||
|
||||
// DeleteUrlMap deletes a url map by name.
|
||||
func (gce *GCECloud) DeleteUrlMap(name string) error {
|
||||
mc := newUrlMapMetricContext("delete")
|
||||
op, err := gce.service.UrlMaps.Delete(gce.projectID, name).Do()
|
||||
if err != nil {
|
||||
if isHTTPErrorCode(err, http.StatusNotFound) {
|
||||
return nil
|
||||
}
|
||||
return mc.Observe(err)
|
||||
}
|
||||
return gce.waitForGlobalOp(op, mc)
|
||||
}
|
||||
|
||||
// ListUrlMaps lists all UrlMaps in the project.
|
||||
func (gce *GCECloud) ListUrlMaps() (*compute.UrlMapList, error) {
|
||||
mc := newUrlMapMetricContext("list")
|
||||
// TODO: use PageToken to list all not just the first 500
|
||||
v, err := gce.service.UrlMaps.List(gce.projectID).Do()
|
||||
return v, mc.Observe(err)
|
||||
}
|
276
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_util.go
generated
vendored
Normal file
276
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_util.go
generated
vendored
Normal file
@ -0,0 +1,276 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gce
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
|
||||
"cloud.google.com/go/compute/metadata"
|
||||
compute "google.golang.org/api/compute/v1"
|
||||
"google.golang.org/api/googleapi"
|
||||
)
|
||||
|
||||
type gceInstance struct {
|
||||
Zone string
|
||||
Name string
|
||||
ID uint64
|
||||
Disks []*compute.AttachedDisk
|
||||
Type string
|
||||
}
|
||||
|
||||
var (
|
||||
autoSubnetIPRange = &net.IPNet{
|
||||
IP: net.ParseIP("10.128.0.0"),
|
||||
Mask: net.CIDRMask(9, 32),
|
||||
}
|
||||
)
|
||||
|
||||
var providerIdRE = regexp.MustCompile(`^` + ProviderName + `://([^/]+)/([^/]+)/([^/]+)$`)
|
||||
|
||||
func getProjectAndZone() (string, string, error) {
|
||||
result, err := metadata.Get("instance/zone")
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
parts := strings.Split(result, "/")
|
||||
if len(parts) != 4 {
|
||||
return "", "", fmt.Errorf("unexpected response: %s", result)
|
||||
}
|
||||
zone := parts[3]
|
||||
projectID, err := metadata.ProjectID()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return projectID, zone, nil
|
||||
}
|
||||
|
||||
func (gce *GCECloud) raiseFirewallChangeNeededEvent(svc *v1.Service, cmd string) {
|
||||
msg := fmt.Sprintf("Firewall change required by network admin: `%v`", cmd)
|
||||
if gce.eventRecorder != nil && svc != nil {
|
||||
gce.eventRecorder.Event(svc, v1.EventTypeNormal, "LoadBalancerManualChange", msg)
|
||||
}
|
||||
}
|
||||
|
||||
// FirewallToGCloudCreateCmd generates a gcloud command to create a firewall with specified params
|
||||
func FirewallToGCloudCreateCmd(fw *compute.Firewall, projectID string) string {
|
||||
args := firewallToGcloudArgs(fw, projectID)
|
||||
return fmt.Sprintf("gcloud compute firewall-rules create %v --network %v %v", fw.Name, getNameFromLink(fw.Network), args)
|
||||
}
|
||||
|
||||
// FirewallToGCloudCreateCmd generates a gcloud command to update a firewall to specified params
|
||||
func FirewallToGCloudUpdateCmd(fw *compute.Firewall, projectID string) string {
|
||||
args := firewallToGcloudArgs(fw, projectID)
|
||||
return fmt.Sprintf("gcloud compute firewall-rules update %v %v", fw.Name, args)
|
||||
}
|
||||
|
||||
// FirewallToGCloudCreateCmd generates a gcloud command to delete a firewall to specified params
|
||||
func FirewallToGCloudDeleteCmd(fwName, projectID string) string {
|
||||
return fmt.Sprintf("gcloud compute firewall-rules delete %v --project %v", fwName, projectID)
|
||||
}
|
||||
|
||||
func firewallToGcloudArgs(fw *compute.Firewall, projectID string) string {
|
||||
var allPorts []string
|
||||
for _, a := range fw.Allowed {
|
||||
for _, p := range a.Ports {
|
||||
allPorts = append(allPorts, fmt.Sprintf("%v:%v", a.IPProtocol, p))
|
||||
}
|
||||
}
|
||||
allow := strings.Join(allPorts, ",")
|
||||
srcRngs := strings.Join(fw.SourceRanges, ",")
|
||||
targets := strings.Join(fw.TargetTags, ",")
|
||||
return fmt.Sprintf("--description %q --allow %v --source-ranges %v --target-tags %v --project %v", fw.Description, allow, srcRngs, targets, projectID)
|
||||
}
|
||||
|
||||
// Take a GCE instance 'hostname' and break it down to something that can be fed
|
||||
// to the GCE API client library. Basically this means reducing 'kubernetes-
|
||||
// node-2.c.my-proj.internal' to 'kubernetes-node-2' if necessary.
|
||||
func canonicalizeInstanceName(name string) string {
|
||||
ix := strings.Index(name, ".")
|
||||
if ix != -1 {
|
||||
name = name[:ix]
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
// Returns the last component of a URL, i.e. anything after the last slash
|
||||
// If there is no slash, returns the whole string
|
||||
func lastComponent(s string) string {
|
||||
lastSlash := strings.LastIndex(s, "/")
|
||||
if lastSlash != -1 {
|
||||
s = s[lastSlash+1:]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// mapNodeNameToInstanceName maps a k8s NodeName to a GCE Instance Name
|
||||
// This is a simple string cast.
|
||||
func mapNodeNameToInstanceName(nodeName types.NodeName) string {
|
||||
return string(nodeName)
|
||||
}
|
||||
|
||||
// mapInstanceToNodeName maps a GCE Instance to a k8s NodeName
|
||||
func mapInstanceToNodeName(instance *compute.Instance) types.NodeName {
|
||||
return types.NodeName(instance.Name)
|
||||
}
|
||||
|
||||
// GetGCERegion returns region of the gce zone. Zone names
|
||||
// are of the form: ${region-name}-${ix}.
|
||||
// For example, "us-central1-b" has a region of "us-central1".
|
||||
// So we look for the last '-' and trim to just before that.
|
||||
func GetGCERegion(zone string) (string, error) {
|
||||
ix := strings.LastIndex(zone, "-")
|
||||
if ix == -1 {
|
||||
return "", fmt.Errorf("unexpected zone: %s", zone)
|
||||
}
|
||||
return zone[:ix], nil
|
||||
}
|
||||
|
||||
func isHTTPErrorCode(err error, code int) bool {
|
||||
apiErr, ok := err.(*googleapi.Error)
|
||||
return ok && apiErr.Code == code
|
||||
}
|
||||
|
||||
func isInUsedByError(err error) bool {
|
||||
apiErr, ok := err.(*googleapi.Error)
|
||||
if !ok || apiErr.Code != http.StatusBadRequest {
|
||||
return false
|
||||
}
|
||||
return strings.Contains(apiErr.Message, "being used by")
|
||||
}
|
||||
|
||||
// splitProviderID splits a provider's id into core components.
|
||||
// A providerID is build out of '${ProviderName}://${project-id}/${zone}/${instance-name}'
|
||||
// See cloudprovider.GetInstanceProviderID.
|
||||
func splitProviderID(providerID string) (project, zone, instance string, err error) {
|
||||
matches := providerIdRE.FindStringSubmatch(providerID)
|
||||
if len(matches) != 4 {
|
||||
return "", "", "", errors.New("error splitting providerID")
|
||||
}
|
||||
return matches[1], matches[2], matches[3], nil
|
||||
}
|
||||
|
||||
func equalStringSets(x, y []string) bool {
|
||||
if len(x) != len(y) {
|
||||
return false
|
||||
}
|
||||
xString := sets.NewString(x...)
|
||||
yString := sets.NewString(y...)
|
||||
return xString.Equal(yString)
|
||||
}
|
||||
|
||||
func isNotFound(err error) bool {
|
||||
return isHTTPErrorCode(err, http.StatusNotFound)
|
||||
}
|
||||
|
||||
func ignoreNotFound(err error) error {
|
||||
if err == nil || isNotFound(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func isNotFoundOrInUse(err error) bool {
|
||||
return isNotFound(err) || isInUsedByError(err)
|
||||
}
|
||||
|
||||
func isForbidden(err error) bool {
|
||||
return isHTTPErrorCode(err, http.StatusForbidden)
|
||||
}
|
||||
|
||||
func makeGoogleAPINotFoundError(message string) error {
|
||||
return &googleapi.Error{Code: http.StatusNotFound, Message: message}
|
||||
}
|
||||
|
||||
func makeGoogleAPIError(code int, message string) error {
|
||||
return &googleapi.Error{Code: code, Message: message}
|
||||
}
|
||||
|
||||
// TODO(#51665): Remove this once Network Tiers becomes Beta in GCP.
|
||||
func handleAlphaNetworkTierGetError(err error) (string, error) {
|
||||
if isForbidden(err) {
|
||||
// Network tier is still an Alpha feature in GCP, and not every project
|
||||
// is whitelisted to access the API. If we cannot access the API, just
|
||||
// assume the tier is premium.
|
||||
return NetworkTierDefault.ToGCEValue(), nil
|
||||
}
|
||||
// Can't get the network tier, just return an error.
|
||||
return "", err
|
||||
}
|
||||
|
||||
// containsCIDR returns true if outer contains inner.
|
||||
func containsCIDR(outer, inner *net.IPNet) bool {
|
||||
return outer.Contains(firstIPInRange(inner)) && outer.Contains(lastIPInRange(inner))
|
||||
}
|
||||
|
||||
// firstIPInRange returns the first IP in a given IP range.
|
||||
func firstIPInRange(ipNet *net.IPNet) net.IP {
|
||||
return ipNet.IP.Mask(ipNet.Mask)
|
||||
}
|
||||
|
||||
// lastIPInRange returns the last IP in a given IP range.
|
||||
func lastIPInRange(cidr *net.IPNet) net.IP {
|
||||
ip := append([]byte{}, cidr.IP...)
|
||||
for i, b := range cidr.Mask {
|
||||
ip[i] |= ^b
|
||||
}
|
||||
return ip
|
||||
}
|
||||
|
||||
// subnetsInCIDR takes a list of subnets for a single region and
|
||||
// returns subnets which exists in the specified CIDR range.
|
||||
func subnetsInCIDR(subnets []*compute.Subnetwork, cidr *net.IPNet) ([]*compute.Subnetwork, error) {
|
||||
var res []*compute.Subnetwork
|
||||
for _, subnet := range subnets {
|
||||
_, subnetRange, err := net.ParseCIDR(subnet.IpCidrRange)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse CIDR %q for subnet %q: %v", subnet.IpCidrRange, subnet.Name, err)
|
||||
}
|
||||
if containsCIDR(cidr, subnetRange) {
|
||||
res = append(res, subnet)
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
type netType string
|
||||
|
||||
const (
|
||||
netTypeLegacy netType = "LEGACY"
|
||||
netTypeAuto netType = "AUTO"
|
||||
netTypeCustom netType = "CUSTOM"
|
||||
)
|
||||
|
||||
func typeOfNetwork(network *compute.Network) netType {
|
||||
if network.IPv4Range != "" {
|
||||
return netTypeLegacy
|
||||
}
|
||||
|
||||
if network.AutoCreateSubnetworks {
|
||||
return netTypeAuto
|
||||
}
|
||||
|
||||
return netTypeCustom
|
||||
}
|
90
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_util_test.go
generated
vendored
Normal file
90
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_util_test.go
generated
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gce
|
||||
|
||||
import (
|
||||
"net"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
compute "google.golang.org/api/compute/v1"
|
||||
)
|
||||
|
||||
func TestLastIPInRange(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
cidr string
|
||||
want string
|
||||
}{
|
||||
{"10.1.2.3/32", "10.1.2.3"},
|
||||
{"10.1.2.0/31", "10.1.2.1"},
|
||||
{"10.1.0.0/30", "10.1.0.3"},
|
||||
{"10.0.0.0/29", "10.0.0.7"},
|
||||
{"::0/128", "::"},
|
||||
{"::0/127", "::1"},
|
||||
{"::0/126", "::3"},
|
||||
{"::0/120", "::ff"},
|
||||
} {
|
||||
_, c, err := net.ParseCIDR(tc.cidr)
|
||||
if err != nil {
|
||||
t.Errorf("net.ParseCIDR(%v) = _, %v, %v; want nil", tc.cidr, c, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if lastIP := lastIPInRange(c); lastIP.String() != tc.want {
|
||||
t.Errorf("LastIPInRange(%v) = %v; want %v", tc.cidr, lastIP, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubnetsInCIDR(t *testing.T) {
|
||||
subnets := []*compute.Subnetwork{
|
||||
{
|
||||
Name: "A",
|
||||
IpCidrRange: "10.0.0.0/20",
|
||||
},
|
||||
{
|
||||
Name: "B",
|
||||
IpCidrRange: "10.0.16.0/20",
|
||||
},
|
||||
{
|
||||
Name: "C",
|
||||
IpCidrRange: "10.132.0.0/20",
|
||||
},
|
||||
{
|
||||
Name: "D",
|
||||
IpCidrRange: "10.0.32.0/20",
|
||||
},
|
||||
{
|
||||
Name: "E",
|
||||
IpCidrRange: "10.134.0.0/20",
|
||||
},
|
||||
}
|
||||
expectedNames := []string{"C", "E"}
|
||||
|
||||
gotSubs, err := subnetsInCIDR(subnets, autoSubnetIPRange)
|
||||
if err != nil {
|
||||
t.Errorf("autoSubnetInList() = _, %v", err)
|
||||
}
|
||||
|
||||
var gotNames []string
|
||||
for _, v := range gotSubs {
|
||||
gotNames = append(gotNames, v.Name)
|
||||
}
|
||||
if !reflect.DeepEqual(gotNames, expectedNames) {
|
||||
t.Errorf("autoSubnetInList() = %v, expected: %v", gotNames, expectedNames)
|
||||
}
|
||||
}
|
85
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_zones.go
generated
vendored
Normal file
85
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_zones.go
generated
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gce
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
compute "google.golang.org/api/compute/v1"
|
||||
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/kubernetes/pkg/cloudprovider"
|
||||
)
|
||||
|
||||
func newZonesMetricContext(request, region string) *metricContext {
|
||||
return newGenericMetricContext("zones", request, region, unusedMetricLabel, computeV1Version)
|
||||
}
|
||||
|
||||
// GetZone creates a cloudprovider.Zone of the current zone and region
|
||||
func (gce *GCECloud) GetZone() (cloudprovider.Zone, error) {
|
||||
return cloudprovider.Zone{
|
||||
FailureDomain: gce.localZone,
|
||||
Region: gce.region,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetZoneByProviderID implements Zones.GetZoneByProviderID
|
||||
// This is particularly useful in external cloud providers where the kubelet
|
||||
// does not initialize node data.
|
||||
func (gce *GCECloud) GetZoneByProviderID(providerID string) (cloudprovider.Zone, error) {
|
||||
_, zone, _, err := splitProviderID(providerID)
|
||||
if err != nil {
|
||||
return cloudprovider.Zone{}, err
|
||||
}
|
||||
region, err := GetGCERegion(zone)
|
||||
if err != nil {
|
||||
return cloudprovider.Zone{}, err
|
||||
}
|
||||
return cloudprovider.Zone{FailureDomain: zone, Region: region}, nil
|
||||
}
|
||||
|
||||
// GetZoneByNodeName implements Zones.GetZoneByNodeName
|
||||
// This is particularly useful in external cloud providers where the kubelet
|
||||
// does not initialize node data.
|
||||
func (gce *GCECloud) GetZoneByNodeName(nodeName types.NodeName) (cloudprovider.Zone, error) {
|
||||
instanceName := mapNodeNameToInstanceName(nodeName)
|
||||
instance, err := gce.getInstanceByName(instanceName)
|
||||
if err != nil {
|
||||
return cloudprovider.Zone{}, err
|
||||
}
|
||||
region, err := GetGCERegion(instance.Zone)
|
||||
if err != nil {
|
||||
return cloudprovider.Zone{}, err
|
||||
}
|
||||
return cloudprovider.Zone{FailureDomain: instance.Zone, Region: region}, nil
|
||||
}
|
||||
|
||||
// ListZonesInRegion returns all zones in a GCP region
|
||||
func (gce *GCECloud) ListZonesInRegion(region string) ([]*compute.Zone, error) {
|
||||
mc := newZonesMetricContext("list", region)
|
||||
filter := fmt.Sprintf("region eq %v", gce.getRegionLink(region))
|
||||
list, err := gce.service.Zones.List(gce.projectID).Filter(filter).Do()
|
||||
if err != nil {
|
||||
return nil, mc.Observe(err)
|
||||
}
|
||||
return list.Items, mc.Observe(err)
|
||||
}
|
||||
|
||||
func (gce *GCECloud) getRegionLink(region string) string {
|
||||
return gce.service.BasePath + strings.Join([]string{gce.projectID, "regions", region}, "/")
|
||||
}
|
105
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/metrics.go
generated
vendored
Normal file
105
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/metrics.go
generated
vendored
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
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 gce
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
const (
|
||||
// Version strings for recording metrics.
|
||||
computeV1Version = "v1"
|
||||
computeAlphaVersion = "alpha"
|
||||
computeBetaVersion = "beta"
|
||||
)
|
||||
|
||||
type apiCallMetrics struct {
|
||||
latency *prometheus.HistogramVec
|
||||
errors *prometheus.CounterVec
|
||||
}
|
||||
|
||||
var (
|
||||
metricLabels = []string{
|
||||
"request", // API function that is begin invoked.
|
||||
"region", // region (optional).
|
||||
"zone", // zone (optional).
|
||||
"version", // API version.
|
||||
}
|
||||
|
||||
apiMetrics = registerAPIMetrics(metricLabels...)
|
||||
)
|
||||
|
||||
type metricContext struct {
|
||||
start time.Time
|
||||
// The cardinalities of attributes and metricLabels (defined above) must
|
||||
// match, or prometheus will panic.
|
||||
attributes []string
|
||||
}
|
||||
|
||||
// Value for an unused label in the metric dimension.
|
||||
const unusedMetricLabel = "<n/a>"
|
||||
|
||||
// Observe the result of a API call.
|
||||
func (mc *metricContext) Observe(err error) error {
|
||||
apiMetrics.latency.WithLabelValues(mc.attributes...).Observe(
|
||||
time.Since(mc.start).Seconds())
|
||||
if err != nil {
|
||||
apiMetrics.errors.WithLabelValues(mc.attributes...).Inc()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func newGenericMetricContext(prefix, request, region, zone, version string) *metricContext {
|
||||
if len(zone) == 0 {
|
||||
zone = unusedMetricLabel
|
||||
}
|
||||
if len(region) == 0 {
|
||||
region = unusedMetricLabel
|
||||
}
|
||||
return &metricContext{
|
||||
start: time.Now(),
|
||||
attributes: []string{prefix + "_" + request, region, zone, version},
|
||||
}
|
||||
}
|
||||
|
||||
// registerApiMetrics adds metrics definitions for a category of API calls.
|
||||
func registerAPIMetrics(attributes ...string) *apiCallMetrics {
|
||||
metrics := &apiCallMetrics{
|
||||
latency: prometheus.NewHistogramVec(
|
||||
prometheus.HistogramOpts{
|
||||
Name: "cloudprovider_gce_api_request_duration_seconds",
|
||||
Help: "Latency of a GCE API call",
|
||||
},
|
||||
attributes,
|
||||
),
|
||||
errors: prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "cloudprovider_gce_api_request_errors",
|
||||
Help: "Number of errors for an API call",
|
||||
},
|
||||
attributes,
|
||||
),
|
||||
}
|
||||
|
||||
prometheus.MustRegister(metrics.latency)
|
||||
prometheus.MustRegister(metrics.errors)
|
||||
|
||||
return metrics
|
||||
}
|
28
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/metrics_test.go
generated
vendored
Normal file
28
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/metrics_test.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gce
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestVerifyMetricLabelCardinality(t *testing.T) {
|
||||
mc := newGenericMetricContext("foo", "get", "us-central1", "<n/a>", "alpha")
|
||||
assert.Len(t, mc.attributes, len(metricLabels), "cardinalities of labels and values must match")
|
||||
}
|
112
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/token_source.go
generated
vendored
Normal file
112
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/token_source.go
generated
vendored
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
Copyright 2015 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 gce
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"k8s.io/client-go/util/flowcontrol"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/google"
|
||||
"google.golang.org/api/googleapi"
|
||||
)
|
||||
|
||||
const (
|
||||
// Max QPS to allow through to the token URL.
|
||||
tokenURLQPS = .05 // back off to once every 20 seconds when failing
|
||||
// Maximum burst of requests to token URL before limiting.
|
||||
tokenURLBurst = 3
|
||||
)
|
||||
|
||||
var (
|
||||
getTokenCounter = prometheus.NewCounter(
|
||||
prometheus.CounterOpts{
|
||||
Name: "get_token_count",
|
||||
Help: "Counter of total Token() requests to the alternate token source",
|
||||
},
|
||||
)
|
||||
getTokenFailCounter = prometheus.NewCounter(
|
||||
prometheus.CounterOpts{
|
||||
Name: "get_token_fail_count",
|
||||
Help: "Counter of failed Token() requests to the alternate token source",
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
func init() {
|
||||
prometheus.MustRegister(getTokenCounter)
|
||||
prometheus.MustRegister(getTokenFailCounter)
|
||||
}
|
||||
|
||||
type AltTokenSource struct {
|
||||
oauthClient *http.Client
|
||||
tokenURL string
|
||||
tokenBody string
|
||||
throttle flowcontrol.RateLimiter
|
||||
}
|
||||
|
||||
func (a *AltTokenSource) Token() (*oauth2.Token, error) {
|
||||
a.throttle.Accept()
|
||||
getTokenCounter.Inc()
|
||||
t, err := a.token()
|
||||
if err != nil {
|
||||
getTokenFailCounter.Inc()
|
||||
}
|
||||
return t, err
|
||||
}
|
||||
|
||||
func (a *AltTokenSource) token() (*oauth2.Token, error) {
|
||||
req, err := http.NewRequest("POST", a.tokenURL, strings.NewReader(a.tokenBody))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := a.oauthClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if err := googleapi.CheckResponse(res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var tok struct {
|
||||
AccessToken string `json:"accessToken"`
|
||||
ExpireTime time.Time `json:"expireTime"`
|
||||
}
|
||||
if err := json.NewDecoder(res.Body).Decode(&tok); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &oauth2.Token{
|
||||
AccessToken: tok.AccessToken,
|
||||
Expiry: tok.ExpireTime,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewAltTokenSource(tokenURL, tokenBody string) oauth2.TokenSource {
|
||||
client := oauth2.NewClient(oauth2.NoContext, google.ComputeTokenSource(""))
|
||||
a := &AltTokenSource{
|
||||
oauthClient: client,
|
||||
tokenURL: tokenURL,
|
||||
tokenBody: tokenBody,
|
||||
throttle: flowcontrol.NewTokenBucketRateLimiter(tokenURLQPS, tokenURLBurst),
|
||||
}
|
||||
return oauth2.ReuseTokenSource(nil, a)
|
||||
}
|
Reference in New Issue
Block a user