| /* |
| 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" |
| "reflect" |
| "strings" |
| "testing" |
| |
| "golang.org/x/oauth2/google" |
| |
| cloudprovider "k8s.io/cloud-provider" |
| ) |
| |
| 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 |
| regional = 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, |
| Regional: 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 := &Cloud{ |
| localZone: zoneName, |
| region: regionName, |
| } |
| zones, ok := gce.Zones() |
| if !ok { |
| t.Fatalf("Unexpected missing zones impl") |
| } |
| zone, err := zones.GetZone(context.TODO()) |
| 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 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 := &Cloud{ |
| localZone: "us-central1-f", |
| region: "us-central1", |
| } |
| for _, test := range tests { |
| zone, err := gce.GetZoneByProviderID(context.TODO(), 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, |
| Regional: 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: "Regional", |
| config: func() ConfigGlobal { |
| v := configBoilerplate |
| v.Regional = true |
| return v |
| }, |
| cloud: func() CloudConfig { |
| v := cloudBoilerplate |
| v.Regional = true |
| 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 TestNewAlphaFeatureGate(t *testing.T) { |
| testCases := []struct { |
| alphaFeatures []string |
| expectEnabled []string |
| expectDisabled []string |
| }{ |
| // enable foo bar |
| { |
| alphaFeatures: []string{"foo", "bar"}, |
| expectEnabled: []string{"foo", "bar"}, |
| expectDisabled: []string{"aaa"}, |
| }, |
| // no alpha feature |
| { |
| alphaFeatures: []string{}, |
| expectEnabled: []string{}, |
| expectDisabled: []string{"foo", "bar"}, |
| }, |
| // unsupported alpha feature |
| { |
| alphaFeatures: []string{"aaa", "foo"}, |
| expectEnabled: []string{"foo"}, |
| expectDisabled: []string{}, |
| }, |
| // enable foo |
| { |
| alphaFeatures: []string{"foo"}, |
| expectEnabled: []string{"foo"}, |
| expectDisabled: []string{"bar"}, |
| }, |
| } |
| |
| for _, tc := range testCases { |
| featureGate := NewAlphaFeatureGate(tc.alphaFeatures) |
| |
| 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) |
| } |
| } |
| } |
| } |
| |
| 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) |
| } |
| } |
| } |