Repository: kubernetes-sigs/cloud-provider-azure (go)

Issue:
Add support for InstancesV2 interface
Refer PR:

* https://github.com/kubernetes/kubernetes/pull/92367 (merged)
* https://github.com/kubernetes/kubernetes/pull/91319 (merged)

After these PR gets merged, we could add InstancesV2 support for Cloud Provider Azure. The codes should be implemented in k/k and vendor into cloud-provider-azure repo.

Pull request description:
Implement cloudprovider.InstancesV2 interface
<!--  Thanks for sending a pull request!  Here are some tips for you:

1. If this is your first time, please read our contributor guidelines: https://git.k8s.io/community/contributors/guide#your-first-contribution and developer guide https://git.k8s.io/community/contributors/devel/development.md#development-guide
2. Please label this pull request according to what type of issue you are addressing, especially if this is a release targeted pull request. For reference on required PR/issue labels, read here:
https://git.k8s.io/community/contributors/devel/release.md#issue-kind-label
3. Ensure you have added or ran the appropriate tests for your PR: https://git.k8s.io/community/contributors/devel/testing.md
4. If you want *faster* PR reviews, read how: https://git.k8s.io/community/contributors/guide/pull-requests.md#best-practices-for-faster-reviews
5. Follow the instructions for writing a release note: https://git.k8s.io/community/contributors/guide/release-notes.md
6. If the PR is unfinished, see how to mark it: https://git.k8s.io/community/contributors/guide/pull-requests.md#marking-unfinished-pull-requests
-->

**What type of PR is this?**
> Uncomment only one ` /kind <>` line, hit enter to put that in a new line, and remove leading whitespaces from that line:
>
> /kind api-change
> /kind bug
> /kind cleanup
> /kind design
> /kind documentation
> /kind failing-test
> /kind feature
> /kind flake
/kind feature

**What this PR does / why we need it**:
Implement cloudprovider.InstancesV2 interface

**Which issue(s) this PR fixes**:
<!-- 
*Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
_If PR is about `failing-tests or flakes`, please post the related issues/tests in a comment and do not use `Fixes`_*
-->
Fixes #344

**Special notes for your reviewer**:


**Release note**:
```
Implement cloudprovider.InstancesV2 interface
```


Patch:
diff --git a/pkg/azureclients/interfaceclient/azure_interfaceclient.go b/pkg/azureclients/interfaceclient/azure_interfaceclient.go
index 106f54cfc..25934297c 100644
--- a/pkg/azureclients/interfaceclient/azure_interfaceclient.go
+++ b/pkg/azureclients/interfaceclient/azure_interfaceclient.go
@@ -20,13 +20,11 @@ import (
 	"context"
 	"fmt"
 	"net/http"
-	"strings"
 	"time"
 
 	"github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network"
 	"github.com/Azure/go-autorest/autorest"
 	"github.com/Azure/go-autorest/autorest/azure"
-	"github.com/Azure/go-autorest/autorest/to"
 
 	"k8s.io/client-go/util/flowcontrol"
 	"k8s.io/klog/v2"
@@ -43,7 +41,6 @@ var _ Interface = &Client{}
 type Client struct {
 	armClient      armclient.Interface
 	subscriptionID string
-	cloudName      string
 
 	// Rate limiting configures.
 	rateLimiterReader flowcontrol.RateLimiter
@@ -58,11 +55,7 @@ type Client struct {
 func New(config *azclients.ClientConfig) *Client {
 	baseURI := config.ResourceManagerEndpoint
 	authorizer := config.Authorizer
-	apiVersion := APIVersion
-	if strings.EqualFold(config.CloudName, AzureStackCloudName) {
-		apiVersion = AzureStackCloudAPIVersion
-	}
-	armClient := armclient.New(authorizer, baseURI, config.UserAgent, apiVersion, config.Location, config.Backoff)
+	armClient := armclient.New(authorizer, baseURI, config.UserAgent, APIVersion, config.Location, config.Backoff)
 	rateLimiterReader, rateLimiterWriter := azclients.NewRateLimiter(config.RateLimitConfig)
 
 	klog.V(2).Infof("Azure InterfacesClient (read ops) using rate limit config: QPS=%g, bucket=%d",
@@ -77,7 +70,6 @@ func New(config *azclients.ClientConfig) *Client {
 		rateLimiterReader: rateLimiterReader,
 		rateLimiterWriter: rateLimiterWriter,
 		subscriptionID:    config.SubscriptionID,
-		cloudName:         config.CloudName,
 	}
 
 	return client
@@ -186,12 +178,8 @@ func (c *Client) getVMSSNetworkInterface(ctx context.Context, resourceGroupName
 	)
 
 	result := network.Interface{}
-	computeAPIVersion := ComputeAPIVersion
-	if strings.EqualFold(c.cloudName, AzureStackCloudName) {
-		computeAPIVersion = AzureStackComputeAPIVersion
-	}
 	queryParameters := map[string]interface{}{
-		"api-version": computeAPIVersion,
+		"api-version": ComputeAPIVersion,
 	}
 	if len(expand) > 0 {
 		queryParameters["$expand"] = autorest.Encode("query", expand)
@@ -258,15 +246,7 @@ func (c *Client) createOrUpdateInterface(ctx context.Context, resourceGroupName
 		"Microsoft.Network/networkInterfaces",
 		networkInterfaceName,
 	)
-	decorators := []autorest.PrepareDecorator{
-		autorest.WithPathParameters("{resourceID}", map[string]interface{}{"resourceID": resourceID}),
-		autorest.WithJSON(parameters),
-	}
-	if to.String(parameters.Etag) != "" {
-		decorators = append(decorators, autorest.WithHeader("If-Match", autorest.String(to.String(parameters.Etag))))
-	}
-
-	response, rerr := c.armClient.PutResourceWithDecorators(ctx, resourceID, parameters, decorators)
+	response, rerr := c.armClient.PutResource(ctx, resourceID, parameters)
 	defer c.armClient.CloseResponse(ctx, response)
 	if rerr != nil {
 		klog.V(5).Infof("Received error in %s: resourceID: %s, error: %s", "nic.put.request", resourceID, rerr.Error())
diff --git a/pkg/azureclients/interfaceclient/interface.go b/pkg/azureclients/interfaceclient/interface.go
index a19d4c169..71302be37 100644
--- a/pkg/azureclients/interfaceclient/interface.go
+++ b/pkg/azureclients/interfaceclient/interface.go
@@ -27,16 +27,9 @@ import (
 const (
 	// APIVersion is the API version for network.
 	APIVersion = "2019-06-01"
-	// AzureStackCloudAPIVersion is the API version for Azure Stack
-	AzureStackCloudAPIVersion = "2018-11-01"
 
 	// ComputeAPIVersion is the API version for compute. It is required to get VMSS network interface.
 	ComputeAPIVersion = "2017-03-30"
-	// AzureStackComputeAPIVersion is the API version for compute for Azure Stack. It is required to get VMSS network interface.
-	AzureStackComputeAPIVersion = "2018-11-01"
-
-	// AzureStackCloudName is the cloud name of Azure Stack
-	AzureStackCloudName = "AZURESTACKCLOUD"
 )
 
 // Interface is the client interface for NetworkInterface.
diff --git a/pkg/provider/azure.go b/pkg/provider/azure.go
index 774c33c88..997d55980 100644
--- a/pkg/provider/azure.go
+++ b/pkg/provider/azure.go
@@ -686,9 +686,8 @@ func (az *Cloud) Instances() (cloudprovider.Instances, bool) {
 }
 
 // InstancesV2 returns an instancesV2 interface. Also returns true if the interface is supported, false otherwise.
-// TODO: implement ONLY for external cloud provider
 func (az *Cloud) InstancesV2() (cloudprovider.InstancesV2, bool) {
-	return nil, false
+	return az, true
 }
 
 // Zones returns a zones interface. Also returns true if the interface is supported, false otherwise.
diff --git a/pkg/provider/azure_instances.go b/pkg/provider/azure_instances.go
index 8b3abcdef..4dac91f0a 100644
--- a/pkg/provider/azure_instances.go
+++ b/pkg/provider/azure_instances.go
@@ -207,6 +207,25 @@ func (az *Cloud) InstanceExistsByProviderID(ctx context.Context, providerID stri
 	return true, nil
 }
 
+// InstanceExists returns true if the instance for the given node exists according to the cloud provider.
+// Use the node.name or node.spec.providerID field to find the node in the cloud provider.
+func (az *Cloud) InstanceExists(ctx context.Context, node *v1.Node) (bool, error) {
+	if node == nil {
+		return false, nil
+	}
+	providerID := node.Spec.ProviderID
+	if providerID == "" {
+		var err error
+		providerID, err = az.getNodeProviderIDByNodeName(ctx, types.NodeName(node.Name))
+		if err != nil {
+			klog.Errorf("InstanceExists: failed to get the provider ID by node name %s: %v", node.Name, err)
+			return false, err
+		}
+	}
+
+	return az.InstanceExistsByProviderID(ctx, providerID)
+}
+
 // InstanceShutdownByProviderID returns true if the instance is in safe state to detach volumes
 func (az *Cloud) InstanceShutdownByProviderID(ctx context.Context, providerID string) (bool, error) {
 	if providerID == "" {
@@ -238,6 +257,25 @@ func (az *Cloud) InstanceShutdownByProviderID(ctx context.Context, providerID st
 	return status == vmPowerStateStopped || status == vmPowerStateDeallocated || status == vmPowerStateDeallocating, nil
 }
 
+// InstanceShutdown returns true if the instance is shutdown according to the cloud provider.
+// Use the node.name or node.spec.providerID field to find the node in the cloud provider.
+func (az *Cloud) InstanceShutdown(ctx context.Context, node *v1.Node) (bool, error) {
+	if node == nil {
+		return false, nil
+	}
+	providerID := node.Spec.ProviderID
+	if providerID == "" {
+		var err error
+		providerID, err = az.getNodeProviderIDByNodeName(ctx, types.NodeName(node.Name))
+		if err != nil {
+			klog.Errorf("InstanceShutdown: failed to get the provider ID by node name %s: %v", node.Name, err)
+			return false, err
+		}
+	}
+
+	return az.InstanceShutdownByProviderID(ctx, providerID)
+}
+
 func (az *Cloud) isCurrentInstance(name types.NodeName, metadataVMName string) (bool, error) {
 	var err error
 	nodeName := mapNodeNameToVMName(name)
@@ -304,6 +342,15 @@ func (az *Cloud) InstanceID(ctx context.Context, name types.NodeName) (string, e
 	return az.VMSet.GetInstanceIDByNodeName(nodeName)
 }
 
+func (az *Cloud) getNodeProviderIDByNodeName(ctx context.Context, name types.NodeName) (string, error) {
+	providerID, err := cloudprovider.GetInstanceProviderID(ctx, az, name)
+	if err != nil {
+		return "", fmt.Errorf("failed to get the provider ID of the node %s: %v", string(name), err)
+	}
+
+	return providerID, nil
+}
+
 func (az *Cloud) getLocalInstanceProviderID(metadata *InstanceMetadata, nodeName string) (string, error) {
 	// Get resource group name and subscription ID.
 	resourceGroup := strings.ToLower(metadata.Compute.ResourceGroup)
@@ -407,6 +454,52 @@ func (az *Cloud) CurrentNodeName(ctx context.Context, hostname string) (types.No
 	return types.NodeName(hostname), nil
 }
 
+// InstanceMetadata returns the instance's metadata. The values returned in InstanceMetadata are
+// translated into specific fields in the Node object on registration.
+// Use the node.name or node.spec.providerID field to find the node in the cloud provider.
+func (az *Cloud) InstanceMetadata(ctx context.Context, node *v1.Node) (*cloudprovider.InstanceMetadata, error) {
+	if node == nil {
+		return &cloudprovider.InstanceMetadata{}, nil
+	}
+
+	meta := cloudprovider.InstanceMetadata{}
+
+	if node.Spec.ProviderID != "" {
+		meta.ProviderID = node.Spec.ProviderID
+	} else {
+		providerID, err := az.getNodeProviderIDByNodeName(ctx, types.NodeName(node.Name))
+		if err != nil {
+			klog.Errorf("InstanceMetadata: failed to get the provider ID by node name %s: %v", node.Name, err)
+			return nil, err
+		}
+		meta.ProviderID = providerID
+	}
+
+	instanceType, err := az.InstanceType(ctx, types.NodeName(node.Name))
+	if err != nil {
+		klog.Errorf("InstanceMetadata: failed to get the instance type of %s: %v", node.Name, err)
+		return &cloudprovider.InstanceMetadata{}, err
+	}
+	meta.InstanceType = instanceType
+
+	nodeAddresses, err := az.NodeAddresses(ctx, types.NodeName(node.Name))
+	if err != nil {
+		klog.Errorf("InstanceMetadata: failed to get the node address of %s: %v", node.Name, err)
+		return &cloudprovider.InstanceMetadata{}, err
+	}
+	meta.NodeAddresses = nodeAddresses
+
+	zone, err := az.GetZoneByNodeName(ctx, types.NodeName(node.Name))
+	if err != nil {
+		klog.Errorf("InstanceMetadata: failed to get the node zone of %s: %v", node.Name, err)
+		return &cloudprovider.InstanceMetadata{}, err
+	}
+	meta.Zone = zone.FailureDomain
+	meta.Region = zone.Region
+
+	return &meta, nil
+}
+
 // mapNodeNameToVMName maps a k8s NodeName to an Azure VM Name
 // This is a simple string cast.
 func mapNodeNameToVMName(nodeName types.NodeName) string {


Test patch:
diff --git a/pkg/azureclients/interfaceclient/azure_interfaceclient_test.go b/pkg/azureclients/interfaceclient/azure_interfaceclient_test.go
index 142021cfc..12a4a583d 100644
--- a/pkg/azureclients/interfaceclient/azure_interfaceclient_test.go
+++ b/pkg/azureclients/interfaceclient/azure_interfaceclient_test.go
@@ -58,27 +58,6 @@ func TestNew(t *testing.T) {
 	assert.NotEmpty(t, interfaceClient.rateLimiterWriter)
 }
 
-func TestNewAzureStack(t *testing.T) {
-	config := &azclients.ClientConfig{
-		CloudName:               "AZURESTACKCLOUD",
-		SubscriptionID:          "sub",
-		ResourceManagerEndpoint: "endpoint",
-		Location:                "eastus",
-		RateLimitConfig: &azclients.RateLimitConfig{
-			CloudProviderRateLimit:            true,
-			CloudProviderRateLimitQPS:         0.5,
-			CloudProviderRateLimitBucket:      1,
-			CloudProviderRateLimitQPSWrite:    0.5,
-			CloudProviderRateLimitBucketWrite: 1,
-		},
-		Backoff: &retry.Backoff{Steps: 1},
-	}
-
-	interfaceClient := New(config)
-	assert.Equal(t, "AZURESTACKCLOUD", interfaceClient.cloudName)
-	assert.Equal(t, "sub", interfaceClient.subscriptionID)
-}
-
 func TestGetNotFound(t *testing.T) {
 	ctrl := gomock.NewController(t)
 	defer ctrl.Finish()
@@ -197,7 +176,7 @@ func TestCreateOrUpdate(t *testing.T) {
 		StatusCode: http.StatusOK,
 		Body:       ioutil.NopCloser(bytes.NewReader([]byte(""))),
 	}
-	armClient.EXPECT().PutResourceWithDecorators(gomock.Any(), to.String(testInterface.ID), testInterface, gomock.Any()).Return(response, nil).Times(1)
+	armClient.EXPECT().PutResource(gomock.Any(), to.String(testInterface.ID), testInterface).Return(response, nil).Times(1)
 	armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(1)
 
 	nicClient := getTestInterfaceClient(armClient)
@@ -215,7 +194,7 @@ func TestCreateOrUpdate(t *testing.T) {
 		RetryAfter:     time.Unix(100, 0),
 	}
 
-	armClient.EXPECT().PutResourceWithDecorators(gomock.Any(), to.String(testInterface.ID), testInterface, gomock.Any()).Return(response, noContentErr).Times(1)
+	armClient.EXPECT().PutResource(gomock.Any(), to.String(testInterface.ID), testInterface).Return(response, noContentErr).Times(1)
 	armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(1)
 	rerr = nicClient.CreateOrUpdate(context.TODO(), "rg", "nic1", testInterface)
 	assert.Equal(t, noContentErr, rerr)
diff --git a/pkg/provider/azure_instances_test.go b/pkg/provider/azure_instances_test.go
index 87388c216..511593355 100644
--- a/pkg/provider/azure_instances_test.go
+++ b/pkg/provider/azure_instances_test.go
@@ -30,6 +30,7 @@ import (
 	"github.com/stretchr/testify/assert"
 
 	v1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/types"
 	cloudprovider "k8s.io/cloud-provider"
 
@@ -784,3 +785,66 @@ func TestCurrentNodeName(t *testing.T) {
 	assert.Equal(t, types.NodeName(hostname), nodeName)
 	assert.NoError(t, err)
 }
+
+func TestInstanceMetadata(t *testing.T) {
+	ctrl := gomock.NewController(t)
+	defer ctrl.Finish()
+
+	t.Run("", func(t *testing.T) {
+		cloud := GetTestCloud(ctrl)
+		expectedVM := buildDefaultTestVirtualMachine("as", []string{"/subscriptions/subscription/resourceGroups/rg/providers/Microsoft.Network/networkInterfaces/k8s-agentpool1-00000000-nic-1"})
+		expectedVM.HardwareProfile = &compute.HardwareProfile{
+			VMSize: compute.VirtualMachineSizeTypesBasicA0,
+		}
+		expectedVM.Location = to.StringPtr("westus2")
+		expectedVM.Zones = &[]string{"1"}
+		expectedVM.ID = to.StringPtr("/subscriptions/subscription/resourceGroups/rg/providers/Microsoft.Compute/VirtualMachines/vm")
+		mockVMClient := cloud.VirtualMachinesClient.(*mockvmclient.MockInterface)
+		mockVMClient.EXPECT().Get(gomock.Any(), cloud.ResourceGroup, "vm", gomock.Any()).Return(expectedVM, nil)
+		expectedNIC := buildDefaultTestInterface(true, []string{})
+		(*expectedNIC.IPConfigurations)[0].PrivateIPAddress = to.StringPtr("1.2.3.4")
+		(*expectedNIC.IPConfigurations)[0].PublicIPAddress = &network.PublicIPAddress{
+			ID: to.StringPtr("pip"),
+			PublicIPAddressPropertiesFormat: &network.PublicIPAddressPropertiesFormat{
+				IPAddress: to.StringPtr("5.6.7.8"),
+			},
+		}
+		mockNICClient := cloud.InterfacesClient.(*mockinterfaceclient.MockInterface)
+		mockNICClient.EXPECT().Get(gomock.Any(), cloud.ResourceGroup, "k8s-agentpool1-00000000-nic-1", gomock.Any()).Return(expectedNIC, nil)
+		expectedPIP := network.PublicIPAddress{
+			PublicIPAddressPropertiesFormat: &network.PublicIPAddressPropertiesFormat{
+				IPAddress: to.StringPtr("5.6.7.8"),
+			},
+		}
+		mockPIPClient := cloud.PublicIPAddressesClient.(*mockpublicipclient.MockInterface)
+		mockPIPClient.EXPECT().Get(gomock.Any(), cloud.ResourceGroup, "pip", gomock.Any()).Return(expectedPIP, nil)
+
+		expectedMetadata := cloudprovider.InstanceMetadata{
+			ProviderID:   "azure:///subscriptions/subscription/resourceGroups/rg/providers/Microsoft.Compute/VirtualMachines/vm",
+			InstanceType: string(compute.VirtualMachineSizeTypesBasicA0),
+			NodeAddresses: []v1.NodeAddress{
+				{
+					Type:    v1.NodeInternalIP,
+					Address: "1.2.3.4",
+				},
+				{
+					Type:    v1.NodeHostName,
+					Address: "vm",
+				},
+				{
+					Type:    v1.NodeExternalIP,
+					Address: "5.6.7.8",
+				},
+			},
+			Zone:   "westus2-1",
+			Region: "westus2",
+		}
+		meta, err := cloud.InstanceMetadata(context.Background(), &v1.Node{
+			ObjectMeta: metav1.ObjectMeta{
+				Name: "vm",
+			},
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, expectedMetadata, *meta)
+	})
+}
diff --git a/tests/e2e/network/ensureloadbalancer.go b/tests/e2e/network/ensureloadbalancer.go
index f7c54d3e8..82b525d36 100644
--- a/tests/e2e/network/ensureloadbalancer.go
+++ b/tests/e2e/network/ensureloadbalancer.go
@@ -30,7 +30,8 @@ import (
 	"k8s.io/apimachinery/pkg/util/uuid"
 	"k8s.io/apimachinery/pkg/util/wait"
 	clientset "k8s.io/client-go/kubernetes"
-	azure "sigs.k8s.io/cloud-provider-azure/pkg/provider"
+
+	"sigs.k8s.io/cloud-provider-azure/pkg/provider"
 	"sigs.k8s.io/cloud-provider-azure/tests/e2e/utils"
 
 	. "github.com/onsi/ginkgo"
@@ -127,9 +128,6 @@ var _ = Describe("Ensure LoadBalancer", func() {
 
 	// Internal w/ IP -> Internal w/ IP
 	It("should support updating internal IP when updating internal service", func() {
-		// TODO: re-enable the test after the underlying SLB issues fixed.
-		Skip("Skip the ILB test scenarios since there're some issues in underlying SLB")
-
 		annotation := map[string]string{
 			azure.ServiceAnnotationLoadBalancerInternal: "true",
 		}
@@ -173,9 +171,6 @@ var _ = Describe("Ensure LoadBalancer", func() {
 
 	// internal w/o IP -> public w/ IP
 	It("should support updating an internal service to a public service with assigned IP", func() {
-		// TODO: re-enable the test after the underlying SLB issues fixed.
-		Skip("Skip the ILB test scenarios since there're some issues in underlying SLB")
-
 		annotation := map[string]string{
 			azure.ServiceAnnotationLoadBalancerInternal: "true",
 		}
@@ -350,9 +345,6 @@ var _ = Describe("Ensure LoadBalancer", func() {
 	})
 
 	It("should support multiple internal services sharing one IP address", func() {
-		// TODO: re-enable the test after the underlying SLB issues fixed.
-		Skip("Skip the ILB test scenarios since there're some issues in underlying SLB")
-
 		annotation := map[string]string{
 			azure.ServiceAnnotationLoadBalancerInternal: "true",
 		}
diff --git a/tests/e2e/network/service_annotations.go b/tests/e2e/network/service_annotations.go
index 0ef437d42..224766bae 100644
--- a/tests/e2e/network/service_annotations.go
+++ b/tests/e2e/network/service_annotations.go
@@ -37,7 +37,7 @@ import (
 	"k8s.io/apimachinery/pkg/util/wait"
 	clientset "k8s.io/client-go/kubernetes"
 
-	azure "sigs.k8s.io/cloud-provider-azure/pkg/provider"
+	"sigs.k8s.io/cloud-provider-azure/pkg/provider"
 	"sigs.k8s.io/cloud-provider-azure/tests/e2e/utils"
 
 	. "github.com/onsi/ginkgo"
@@ -150,9 +150,6 @@ var _ = Describe("Service with annotation", func() {
 	})
 
 	It("should support service annotation 'service.beta.kubernetes.io/azure-load-balancer-internal'", func() {
-		// TODO: re-enable the test after the underlying SLB issues fixed.
-		Skip("Skip the ILB test scenarios since there're some issues in underlying SLB")
-
 		annotation := map[string]string{
 			azure.ServiceAnnotationLoadBalancerInternal: "true",
 		}
@@ -172,9 +169,6 @@ var _ = Describe("Service with annotation", func() {
 	})
 
 	It("should support service annotation 'service.beta.kubernetes.io/azure-load-balancer-internal-subnet'", func() {
-		// TODO: re-enable the test after the underlying SLB issues fixed.
-		Skip("Skip the ILB test scenarios since there're some issues in underlying SLB")
-
 		By("creating environment")
 		subnetName := "lb-subnet"
 
@@ -614,7 +608,6 @@ func createNginxDeploymentManifest(name string, labels map[string]string) (resul
 }
 
 // validate internal source can access to ILB
-// nolint:unused
 func validateInternalLoadBalancer(c clientset.Interface, ns string, url string) error {
 	// create a pod to access to the service
 	utils.Logf("Validating external IP not be public and internal accessible")