mirror of https://github.com/k3s-io/k3s
Merge pull request #50467 from freehan/local-cloud-provider
Automatic merge from submit-queue (batch tested with PRs 51039, 50512, 50546, 50965, 50467) add alpha api gate at gce cloud provider **What this PR does / why we need it**: Add a flag in gce.conf to gate alpha api. Related wrapper function can choose to examine corresponding gate. Currently, there is no new alpha api wrapper funciton being introduced. So there is no supported alpha api. **Release note**: ```release-note None ``` cc: @yujuhong @saad-ali @MrHohnpull/6/head
commit
e2de110e26
|
@ -13,6 +13,7 @@ go_library(
|
|||
"gce.go",
|
||||
"gce_addresses.go",
|
||||
"gce_addresses_fakes.go",
|
||||
"gce_alpha.go",
|
||||
"gce_annotations.go",
|
||||
"gce_backendservice.go",
|
||||
"gce_cert.go",
|
||||
|
|
|
@ -118,6 +118,11 @@ type GCECloud struct {
|
|||
// 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
|
||||
}
|
||||
|
||||
type ServiceManager interface {
|
||||
|
@ -151,6 +156,9 @@ type ConfigFile struct {
|
|||
// Specifying ApiEndpoint will override the default GCE compute API endpoint.
|
||||
ApiEndpoint string `gcfg:"api-endpoint"`
|
||||
LocalZone string `gcfg:"local-zone"`
|
||||
// Possible values: List of api names separated by comma. Default to none.
|
||||
// For example: MyFeatureFlag
|
||||
AlphaFeatures []string `gcfg:"alpha-features"`
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -167,6 +175,7 @@ type CloudConfig struct {
|
|||
NodeInstancePrefix string
|
||||
TokenSource oauth2.TokenSource
|
||||
UseMetadataServer bool
|
||||
AlphaFeatureGate *AlphaFeatureGate
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
@ -245,6 +254,12 @@ func generateCloudConfig(configFile *ConfigFile) (cloudConfig *CloudConfig, err
|
|||
|
||||
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
|
||||
}
|
||||
|
||||
// retrieve projectID and zone
|
||||
|
@ -398,6 +413,7 @@ func CreateGCECloud(config *CloudConfig) (*GCECloud, error) {
|
|||
nodeInstancePrefix: config.NodeInstancePrefix,
|
||||
useMetadataServer: config.UseMetadataServer,
|
||||
operationPollRateLimiter: operationPollRateLimiter,
|
||||
AlphaFeatureGate: config.AlphaFeatureGate,
|
||||
}
|
||||
|
||||
gce.manager = &GCEServiceManager{gce}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
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"
|
||||
)
|
||||
|
||||
// All known alpha features
|
||||
var knownAlphaFeatures = map[string]bool{}
|
||||
|
||||
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)
|
||||
}
|
|
@ -262,6 +262,36 @@ func TestSplitProviderID(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
type generateConfigParams struct {
|
||||
TokenURL string
|
||||
TokenBody string
|
||||
ProjectID string
|
||||
NetworkName string
|
||||
SubnetworkName string
|
||||
NodeTags []string
|
||||
NodeInstancePrefix string
|
||||
Multizone bool
|
||||
ApiEndpoint string
|
||||
LocalZone string
|
||||
AlphaFeatures []string
|
||||
}
|
||||
|
||||
func newGenerateConfigDefaults() *generateConfigParams {
|
||||
return &generateConfigParams{
|
||||
TokenURL: "",
|
||||
TokenBody: "",
|
||||
ProjectID: "project-id",
|
||||
NetworkName: "network-name",
|
||||
SubnetworkName: "",
|
||||
NodeTags: []string{"node-tag"},
|
||||
NodeInstancePrefix: "node-prefix",
|
||||
Multizone: false,
|
||||
ApiEndpoint: "",
|
||||
LocalZone: "us-central1-a",
|
||||
AlphaFeatures: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateCloudConfigs(t *testing.T) {
|
||||
testCases := []struct {
|
||||
TokenURL string
|
||||
|
@ -275,96 +305,10 @@ func TestGenerateCloudConfigs(t *testing.T) {
|
|||
ApiEndpoint string
|
||||
LocalZone string
|
||||
cloudConfig *CloudConfig
|
||||
AlphaFeatures []string
|
||||
}{
|
||||
// default config
|
||||
{
|
||||
TokenURL: "",
|
||||
TokenBody: "",
|
||||
ProjectID: "project-id",
|
||||
NetworkName: "network-name",
|
||||
SubnetworkName: "subnetwork-name",
|
||||
NodeTags: []string{"node-tag"},
|
||||
NodeInstancePrefix: "node-prefix",
|
||||
Multizone: false,
|
||||
ApiEndpoint: "",
|
||||
LocalZone: "us-central1-a",
|
||||
cloudConfig: &CloudConfig{
|
||||
ApiEndpoint: "",
|
||||
ProjectID: "project-id",
|
||||
Region: "us-central1",
|
||||
Zone: "us-central1-a",
|
||||
ManagedZones: []string{"us-central1-a"},
|
||||
NetworkURL: "https://www.googleapis.com/compute/v1/projects/project-id/global/networks/network-name",
|
||||
SubnetworkURL: "https://www.googleapis.com/compute/v1/projects/project-id/regions/us-central1/subnetworks/subnetwork-name",
|
||||
NodeTags: []string{"node-tag"},
|
||||
NodeInstancePrefix: "node-prefix",
|
||||
TokenSource: google.ComputeTokenSource(""),
|
||||
UseMetadataServer: true,
|
||||
},
|
||||
},
|
||||
// nil token source
|
||||
{
|
||||
TokenURL: "nil",
|
||||
TokenBody: "",
|
||||
ProjectID: "project-id",
|
||||
NetworkName: "network-name",
|
||||
SubnetworkName: "subnetwork-name",
|
||||
NodeTags: []string{"node-tag"},
|
||||
NodeInstancePrefix: "node-prefix",
|
||||
Multizone: false,
|
||||
ApiEndpoint: "",
|
||||
LocalZone: "us-central1-a",
|
||||
cloudConfig: &CloudConfig{
|
||||
ApiEndpoint: "",
|
||||
ProjectID: "project-id",
|
||||
Region: "us-central1",
|
||||
Zone: "us-central1-a",
|
||||
ManagedZones: []string{"us-central1-a"},
|
||||
NetworkURL: "https://www.googleapis.com/compute/v1/projects/project-id/global/networks/network-name",
|
||||
SubnetworkURL: "https://www.googleapis.com/compute/v1/projects/project-id/regions/us-central1/subnetworks/subnetwork-name",
|
||||
NodeTags: []string{"node-tag"},
|
||||
NodeInstancePrefix: "node-prefix",
|
||||
TokenSource: nil,
|
||||
UseMetadataServer: true,
|
||||
},
|
||||
},
|
||||
// specified api endpoint
|
||||
{
|
||||
TokenURL: "",
|
||||
TokenBody: "",
|
||||
ProjectID: "project-id",
|
||||
NetworkName: "network-name",
|
||||
SubnetworkName: "subnetwork-name",
|
||||
NodeTags: []string{"node-tag"},
|
||||
NodeInstancePrefix: "node-prefix",
|
||||
Multizone: false,
|
||||
ApiEndpoint: "https://www.googleapis.com/compute/staging_v1/",
|
||||
LocalZone: "us-central1-a",
|
||||
cloudConfig: &CloudConfig{
|
||||
ApiEndpoint: "https://www.googleapis.com/compute/staging_v1/",
|
||||
ProjectID: "project-id",
|
||||
Region: "us-central1",
|
||||
Zone: "us-central1-a",
|
||||
ManagedZones: []string{"us-central1-a"},
|
||||
NetworkURL: "https://www.googleapis.com/compute/staging_v1/projects/project-id/global/networks/network-name",
|
||||
SubnetworkURL: "https://www.googleapis.com/compute/staging_v1/projects/project-id/regions/us-central1/subnetworks/subnetwork-name",
|
||||
NodeTags: []string{"node-tag"},
|
||||
NodeInstancePrefix: "node-prefix",
|
||||
TokenSource: google.ComputeTokenSource(""),
|
||||
UseMetadataServer: true,
|
||||
},
|
||||
},
|
||||
// empty subnet-name
|
||||
{
|
||||
TokenURL: "",
|
||||
TokenBody: "",
|
||||
ProjectID: "project-id",
|
||||
NetworkName: "network-name",
|
||||
SubnetworkName: "",
|
||||
NodeTags: []string{"node-tag"},
|
||||
NodeInstancePrefix: "node-prefix",
|
||||
Multizone: false,
|
||||
ApiEndpoint: "",
|
||||
LocalZone: "us-central1-a",
|
||||
cloudConfig: &CloudConfig{
|
||||
ApiEndpoint: "",
|
||||
ProjectID: "project-id",
|
||||
|
@ -377,20 +321,84 @@ func TestGenerateCloudConfigs(t *testing.T) {
|
|||
NodeInstancePrefix: "node-prefix",
|
||||
TokenSource: google.ComputeTokenSource(""),
|
||||
UseMetadataServer: true,
|
||||
AlphaFeatureGate: &AlphaFeatureGate{map[string]bool{}},
|
||||
},
|
||||
},
|
||||
// nil token source
|
||||
{
|
||||
TokenURL: "nil",
|
||||
cloudConfig: &CloudConfig{
|
||||
ApiEndpoint: "",
|
||||
ProjectID: "project-id",
|
||||
Region: "us-central1",
|
||||
Zone: "us-central1-a",
|
||||
ManagedZones: []string{"us-central1-a"},
|
||||
NetworkURL: "https://www.googleapis.com/compute/v1/projects/project-id/global/networks/network-name",
|
||||
SubnetworkURL: "",
|
||||
NodeTags: []string{"node-tag"},
|
||||
NodeInstancePrefix: "node-prefix",
|
||||
TokenSource: nil,
|
||||
UseMetadataServer: true,
|
||||
AlphaFeatureGate: &AlphaFeatureGate{map[string]bool{}},
|
||||
},
|
||||
},
|
||||
// specified api endpoint
|
||||
{
|
||||
ApiEndpoint: "https://www.googleapis.com/compute/staging_v1/",
|
||||
cloudConfig: &CloudConfig{
|
||||
ApiEndpoint: "https://www.googleapis.com/compute/staging_v1/",
|
||||
ProjectID: "project-id",
|
||||
Region: "us-central1",
|
||||
Zone: "us-central1-a",
|
||||
ManagedZones: []string{"us-central1-a"},
|
||||
NetworkURL: "https://www.googleapis.com/compute/staging_v1/projects/project-id/global/networks/network-name",
|
||||
SubnetworkURL: "",
|
||||
NodeTags: []string{"node-tag"},
|
||||
NodeInstancePrefix: "node-prefix",
|
||||
TokenSource: google.ComputeTokenSource(""),
|
||||
UseMetadataServer: true,
|
||||
AlphaFeatureGate: &AlphaFeatureGate{map[string]bool{}},
|
||||
},
|
||||
},
|
||||
// fqdn subnetname
|
||||
{
|
||||
SubnetworkName: "https://www.googleapis.com/compute/v1/projects/project-id/regions/us-central1/subnetworks/subnetwork-name",
|
||||
cloudConfig: &CloudConfig{
|
||||
ApiEndpoint: "",
|
||||
ProjectID: "project-id",
|
||||
Region: "us-central1",
|
||||
Zone: "us-central1-a",
|
||||
ManagedZones: []string{"us-central1-a"},
|
||||
NetworkURL: "https://www.googleapis.com/compute/v1/projects/project-id/global/networks/network-name",
|
||||
SubnetworkURL: "https://www.googleapis.com/compute/v1/projects/project-id/regions/us-central1/subnetworks/subnetwork-name",
|
||||
NodeTags: []string{"node-tag"},
|
||||
NodeInstancePrefix: "node-prefix",
|
||||
TokenSource: google.ComputeTokenSource(""),
|
||||
UseMetadataServer: true,
|
||||
AlphaFeatureGate: &AlphaFeatureGate{map[string]bool{}},
|
||||
},
|
||||
},
|
||||
// subnetname
|
||||
{
|
||||
SubnetworkName: "subnetwork-name",
|
||||
cloudConfig: &CloudConfig{
|
||||
ApiEndpoint: "",
|
||||
ProjectID: "project-id",
|
||||
Region: "us-central1",
|
||||
Zone: "us-central1-a",
|
||||
ManagedZones: []string{"us-central1-a"},
|
||||
NetworkURL: "https://www.googleapis.com/compute/v1/projects/project-id/global/networks/network-name",
|
||||
SubnetworkURL: "https://www.googleapis.com/compute/v1/projects/project-id/regions/us-central1/subnetworks/subnetwork-name",
|
||||
NodeTags: []string{"node-tag"},
|
||||
NodeInstancePrefix: "node-prefix",
|
||||
TokenSource: google.ComputeTokenSource(""),
|
||||
UseMetadataServer: true,
|
||||
AlphaFeatureGate: &AlphaFeatureGate{map[string]bool{}},
|
||||
},
|
||||
},
|
||||
// multi zone
|
||||
{
|
||||
TokenURL: "",
|
||||
TokenBody: "",
|
||||
ProjectID: "project-id",
|
||||
NetworkName: "network-name",
|
||||
SubnetworkName: "subnetwork-name",
|
||||
NodeTags: []string{"node-tag"},
|
||||
NodeInstancePrefix: "node-prefix",
|
||||
Multizone: true,
|
||||
ApiEndpoint: "",
|
||||
LocalZone: "us-central1-a",
|
||||
cloudConfig: &CloudConfig{
|
||||
ApiEndpoint: "",
|
||||
ProjectID: "project-id",
|
||||
|
@ -398,16 +406,45 @@ func TestGenerateCloudConfigs(t *testing.T) {
|
|||
Zone: "us-central1-a",
|
||||
ManagedZones: nil,
|
||||
NetworkURL: "https://www.googleapis.com/compute/v1/projects/project-id/global/networks/network-name",
|
||||
SubnetworkURL: "https://www.googleapis.com/compute/v1/projects/project-id/regions/us-central1/subnetworks/subnetwork-name",
|
||||
SubnetworkURL: "",
|
||||
NodeTags: []string{"node-tag"},
|
||||
NodeInstancePrefix: "node-prefix",
|
||||
TokenSource: google.ComputeTokenSource(""),
|
||||
UseMetadataServer: true,
|
||||
AlphaFeatureGate: &AlphaFeatureGate{map[string]bool{}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
config := newGenerateConfigDefaults()
|
||||
config.Multizone = tc.Multizone
|
||||
config.ApiEndpoint = tc.ApiEndpoint
|
||||
config.AlphaFeatures = tc.AlphaFeatures
|
||||
config.TokenBody = tc.TokenBody
|
||||
|
||||
if tc.TokenURL != "" {
|
||||
config.TokenURL = tc.TokenURL
|
||||
}
|
||||
if tc.ProjectID != "" {
|
||||
config.ProjectID = tc.ProjectID
|
||||
}
|
||||
if tc.NetworkName != "" {
|
||||
config.NetworkName = tc.NetworkName
|
||||
}
|
||||
if tc.SubnetworkName != "" {
|
||||
config.SubnetworkName = tc.SubnetworkName
|
||||
}
|
||||
if len(tc.NodeTags) > 0 {
|
||||
config.NodeTags = tc.NodeTags
|
||||
}
|
||||
if tc.NodeInstancePrefix != "" {
|
||||
config.NodeInstancePrefix = tc.NodeInstancePrefix
|
||||
}
|
||||
if tc.LocalZone != "" {
|
||||
config.LocalZone = tc.LocalZone
|
||||
}
|
||||
|
||||
cloudConfig, err := generateCloudConfig(&ConfigFile{
|
||||
Global: struct {
|
||||
TokenURL string `gcfg:"token-url"`
|
||||
|
@ -420,17 +457,19 @@ func TestGenerateCloudConfigs(t *testing.T) {
|
|||
Multizone bool `gcfg:"multizone"`
|
||||
ApiEndpoint string `gcfg:"api-endpoint"`
|
||||
LocalZone string `gcfg:"local-zone"`
|
||||
AlphaFeatures []string `gcfg:"alpha-features"`
|
||||
}{
|
||||
TokenURL: tc.TokenURL,
|
||||
TokenBody: tc.TokenBody,
|
||||
ProjectID: tc.ProjectID,
|
||||
NetworkName: tc.NetworkName,
|
||||
SubnetworkName: tc.SubnetworkName,
|
||||
NodeTags: tc.NodeTags,
|
||||
NodeInstancePrefix: tc.NodeInstancePrefix,
|
||||
Multizone: tc.Multizone,
|
||||
ApiEndpoint: tc.ApiEndpoint,
|
||||
LocalZone: tc.LocalZone,
|
||||
TokenURL: config.TokenURL,
|
||||
TokenBody: config.TokenBody,
|
||||
ProjectID: config.ProjectID,
|
||||
NetworkName: config.NetworkName,
|
||||
SubnetworkName: config.SubnetworkName,
|
||||
NodeTags: config.NodeTags,
|
||||
NodeInstancePrefix: config.NodeInstancePrefix,
|
||||
Multizone: config.Multizone,
|
||||
ApiEndpoint: config.ApiEndpoint,
|
||||
LocalZone: config.LocalZone,
|
||||
AlphaFeatures: config.AlphaFeatures,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -492,3 +531,65 @@ func getTestOperation() *computev1.Operation {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue