diff --git a/cluster-autoscaler/Godeps/Godeps.json b/cluster-autoscaler/Godeps/Godeps.json index 20841d2ed049..e8c4553c82fa 100644 --- a/cluster-autoscaler/Godeps/Godeps.json +++ b/cluster-autoscaler/Godeps/Godeps.json @@ -36,6 +36,11 @@ "Comment": "v11.1.1-beta", "Rev": "509eea43b93cec2f3f17acbe2578ef58703923f8" }, + { + "ImportPath": "github.com/Azure/azure-sdk-for-go/arm/resources/resources", + "Comment": "v11.1.1-beta", + "Rev": "509eea43b93cec2f3f17acbe2578ef58703923f8" + }, { "ImportPath": "github.com/Azure/azure-sdk-for-go/arm/storage", "Comment": "v11.1.1-beta", diff --git a/cluster-autoscaler/cloudprovider/azure/README.md b/cluster-autoscaler/cloudprovider/azure/README.md index d9430527e5b4..ffc1e90ee59c 100644 --- a/cluster-autoscaler/cloudprovider/azure/README.md +++ b/cluster-autoscaler/cloudprovider/azure/README.md @@ -1,10 +1,13 @@ # Cluster Autoscaler on Azure -The cluster autoscaler on Azure scales worker nodes within any specified autoscaling group. It will run as a `Deployment` in your cluster. This README will go over some of the necessary steps required to get the cluster autoscaler up and running. +The cluster autoscaler on Azure scales worker nodes within any specified autoscaling group. It will run as a Kubernetes deployment in your cluster. This README will go over some of the necessary steps required to get the cluster autoscaler up and running. ## Kubernetes Version -Cluster autoscaler must run on Kubernetes with Azure VMSS support ([kubernetes#43287](https://github.com/kubernetes/kubernetes/issues/43287)). It is planed in Kubernetes v1.10. +Cluster autoscaler support two VM types with Azure cloud provider: + +- vmss: For kubernetes cluster running on VMSS instances. Azure cloud provider's `vmType` parameter must be configured as 'vmss'. It requires Kubernetes with Azure VMSS support ([kubernetes#43287](https://github.com/kubernetes/kubernetes/issues/43287)), which is planed in Kubernetes v1.10. +- standard: For kubernetes cluster running on VMAS instances. Azure cloud provider's `vmType` parameter must be configured as 'standard'. It only supports Kubernetes cluster deployed via [acs-engine](https://github.com/Azure/acs-engine). ## Permissions @@ -15,188 +18,77 @@ Get azure credentials by running the following command az ad sp create-for-rbac --role="Contributor" --scopes="/subscriptions/" --output json ``` -And fill the values with the result you got into the configmap - -```yaml -apiVersion: v1 -data: - ClientID: - ClientSecret: - ResourceGroup: - SubscriptionID: - TenantID: - ScaleSetName: -kind: ConfigMap -metadata: - name: cluster-autoscaler-azure - namespace: kube-system -``` +## Deployment manifests + +### VMSS deployment + +Pre-requirements: + +- Get credentials from above `permissions` step. +- Get the scale set name which is used for nodes scaling. +- Encode each data with base64. + +Fill the values of cluster-autoscaler-azure secret in [cluster-autoscaler-vmss.yaml](cluster-autoscaler-vmss.yaml), including -Create the configmap by running +- ClientID: `` +- ClientSecret: `` +- ResourceGroup: `` +- SubscriptionID: `` +- TenantID: `` +- NodeGroup: `` + +Note that all data should be encoded with base64. + +Then deploy cluster-autoscaler by running ```sh -kubectl create -f cluster-autoscaler-azure-configmap.yaml +kubectl create -f cluster-autoscaler-vmss.yaml ``` -## Deployment - -```yaml -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: cluster-autoscaler - namespace: kube-system - labels: - app: cluster-autoscaler -spec: - replicas: 1 - selector: - matchLabels: - app: cluster-autoscaler - template: - metadata: - labels: - app: cluster-autoscaler - spec: - containers: - - image: k8s.gcr.io/cluster-autoscaler:{{ ca_version }} - name: cluster-autoscaler - resources: - limits: - cpu: 100m - memory: 300Mi - requests: - cpu: 100m - memory: 300Mi - env: - - name: ARM_SUBSCRIPTION_ID - valueFrom: - configMapKeyRef: - name: cluster-autoscaler-azure - key: SubscriptionID - - name: ARM_RESOURCE_GROUP - valueFrom: - configMapKeyRef: - name: cluster-autoscaler-azure - key: ResourceGroup - - name: ARM_TENANT_ID - valueFrom: - configMapKeyRef: - name: cluster-autoscaler-azure - key: TenantID - - name: ARM_CLIENT_ID - valueFrom: - configMapKeyRef: - name: cluster-autoscaler-azure - key: ClientID - - name: ARM_CLIENT_SECRET - valueFrom: - configMapKeyRef: - name: cluster-autoscaler-azure - key: ClientSecret - - name: ARM_SCALE_SET_NAME - valueFrom: - configMapKeyRef: - name: cluster-autoscaler-azure - key: ScaleSetName - command: - - ./cluster-autoscaler - - --v=4 - - --cloud-provider=azure - - --skip-nodes-with-local-storage=false - - --nodes="1:10:$(ARM_SCALE_SET_NAME)" - volumeMounts: - - name: ssl-certs - mountPath: /etc/ssl/certs/ca-certificates.crt - readOnly: true - imagePullPolicy: "Always" - volumes: - - name: ssl-certs - hostPath: - path: "/etc/ssl/certs/ca-certificates.crt" +To run a CA pod in master node - CA deployment should tolerate the master `taint` and `nodeSelector` should be used to schedule the pods in master node. + +```sh +kubectl create -f cluster-autoscaler-vmss-master.yaml ``` -## Deploy in master node +### Standard deployment + +Pre-requirements: + +- Get credentials from above `permissions` step. +- Get the required paramters from acs-engine deployments (usually under directory `_output/` after running `acs-engine deploy` command) + - Get `APIServerPrivateKey`, `CAPrivateKey`, `ClientPrivateKey` and `KubeConfigPrivateKey` from `azuredeploy.parameters.json` + - If windows nodes are included, also get `WindowsAdminPassword` from acs-engine deployment manifests + - Get the initial Azure deployment name from azure portal. If you have multiple deployments (e.g. have run `acs-engine scale` command), make sure to get the first one + - Get a node pool name for nodes scaling from acs-engine deployment manifests +- Encode each data with base64. + +Fill the values of cluster-autoscaler-azure secret in [cluster-autoscaler-standard.yaml](cluster-autoscaler-standard.yaml), including + +- ClientID: `` +- ClientSecret: `` +- ResourceGroup: `` +- SubscriptionID: `` +- TenantID: `` +- NodeGroup: `` +- Deployment: `` +- APIServerPrivateKey: `` +- CAPrivateKey: `` +- ClientPrivateKey: `` +- KubeConfigPrivateKey: `` +- WindowsAdminPassword: `` + +Note that all data should be encoded with base64. + +Then deploy cluster-autoscaler by running + +```sh +kubectl create -f cluster-autoscaler-standard.yaml +``` To run a CA pod in master node - CA deployment should tolerate the master `taint` and `nodeSelector` should be used to schedule the pods in master node. -```yaml -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: cluster-autoscaler - namespace: kube-system - labels: - app: cluster-autoscaler -spec: - replicas: 1 - selector: - matchLabels: - app: cluster-autoscaler - template: - metadata: - labels: - app: cluster-autoscaler - spec: - tolerations: - - effect: NoSchedule - key: node-role.kubernetes.io/master - nodeSelector: - kubernetes.io/role: master - containers: - - image: k8s.gcr.io/cluster-autoscaler:{{ ca_version }} - name: cluster-autoscaler - resources: - limits: - cpu: 100m - memory: 300Mi - requests: - cpu: 100m - memory: 300Mi - env: - - name: ARM_SUBSCRIPTION_ID - valueFrom: - configMapKeyRef: - name: cluster-autoscaler-azure - key: SubscriptionID - - name: ARM_RESOURCE_GROUP - valueFrom: - configMapKeyRef: - name: cluster-autoscaler-azure - key: ResourceGroup - - name: ARM_TENANT_ID - valueFrom: - configMapKeyRef: - name: cluster-autoscaler-azure - key: TenantID - - name: ARM_CLIENT_ID - valueFrom: - configMapKeyRef: - name: cluster-autoscaler-azure - key: ClientID - - name: ARM_CLIENT_SECRET - valueFrom: - configMapKeyRef: - name: cluster-autoscaler-azure - key: ClientSecret - - name: ARM_SCALE_SET_NAME - valueFrom: - configMapKeyRef: - name: cluster-autoscaler-azure - key: ScaleSetName - command: - - ./cluster-autoscaler - - --v=4 - - --cloud-provider=azure - - --skip-nodes-with-local-storage=false - - --nodes="1:10:$(ARM_SCALE_SET_NAME)" - volumeMounts: - - name: ssl-certs - mountPath: /etc/ssl/certs/ca-certificates.crt - readOnly: true - imagePullPolicy: "Always" - volumes: - - name: ssl-certs - hostPath: - path: "/etc/ssl/certs/ca-certificates.crt" +```sh +kubectl create -f cluster-autoscaler-standard-master.yaml ``` + diff --git a/cluster-autoscaler/cloudprovider/azure/azure_agent_pool.go b/cluster-autoscaler/cloudprovider/azure/azure_agent_pool.go new file mode 100644 index 000000000000..241cade519a4 --- /dev/null +++ b/cluster-autoscaler/cloudprovider/azure/azure_agent_pool.go @@ -0,0 +1,513 @@ +/* +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 azure + +import ( + "fmt" + "math/rand" + "sort" + "strings" + "sync" + "time" + + "github.com/Azure/azure-sdk-for-go/arm/compute" + "github.com/Azure/azure-sdk-for-go/arm/resources/resources" + azStorage "github.com/Azure/azure-sdk-for-go/storage" + "github.com/Azure/go-autorest/autorest/to" + "github.com/golang/glog" + + apiv1 "k8s.io/api/core/v1" + "k8s.io/autoscaler/cluster-autoscaler/cloudprovider" + "k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache" +) + +// AgentPool implements NodeGroup interface for agent pools deployed by acs-engine. +type AgentPool struct { + AzureRef + *AzureManager + + minSize int + maxSize int + + template map[string]interface{} + parameters map[string]interface{} + + mutex sync.Mutex + targetSize int + provisioning bool +} + +// VirtualMachineID contains VMID and ID of a virtual machine. +type VirtualMachineID struct { + ID string + VMID string +} + +// NewAgentPool creates a new AgentPool. +func NewAgentPool(name string, minSize, maxSize int, az *AzureManager) (*AgentPool, error) { + as := &AgentPool{ + AzureRef: AzureRef{ + Name: name, + }, + minSize: minSize, + maxSize: maxSize, + targetSize: -1, + AzureManager: az, + } + + if err := as.initialize(); err != nil { + return nil, err + } + + return as, nil +} + +func (as *AgentPool) initialize() error { + deploy, err := as.deploymentsClient.Get(as.config.ResourceGroup, as.config.Deployment) + if err != nil { + glog.Errorf("deploymentsClient.Get(%s, %s) failed: %v", as.config.ResourceGroup, as.config.Deployment, err) + return err + } + + template, err := as.deploymentsClient.ExportTemplate(as.config.ResourceGroup, as.config.Deployment) + if err != nil { + glog.Errorf("deploymentsClient.ExportTemplate(%s, %s) failed: %v", as.config.ResourceGroup, as.config.Deployment, err) + return err + } + + as.parameters = *deploy.Properties.Parameters + as.preprocessParameters() + + as.template = *template.Template + return normalizeForK8sVMASScalingUp(as.template) +} + +func (as *AgentPool) preprocessParameters() { + // Delete type key from parameters. + for k := range as.parameters { + if v, ok := as.parameters[k].(map[string]interface{}); ok { + delete(v, "type") + } + } + + // fulfill secure parameters. + as.parameters["apiServerPrivateKey"] = map[string]string{"value": as.config.APIServerPrivateKey} + as.parameters["caPrivateKey"] = map[string]string{"value": as.config.CAPrivateKey} + as.parameters["clientPrivateKey"] = map[string]string{"value": as.config.ClientPrivateKey} + as.parameters["kubeConfigPrivateKey"] = map[string]string{"value": as.config.KubeConfigPrivateKey} + as.parameters["servicePrincipalClientId"] = map[string]string{"value": as.config.AADClientID} + as.parameters["servicePrincipalClientSecret"] = map[string]string{"value": as.config.AADClientSecret} + if as.config.WindowsAdminPassword != "" { + as.parameters["windowsAdminPassword"] = map[string]string{"value": as.config.WindowsAdminPassword} + } +} + +// MinSize returns minimum size of the node group. +func (as *AgentPool) MinSize() int { + return as.minSize +} + +// Exist checks if the node group really exists on the cloud provider side. Allows to tell the +// theoretical node group from the real one. +func (as *AgentPool) Exist() bool { + return true +} + +// Create creates the node group on the cloud provider side. +func (as *AgentPool) Create() error { + return cloudprovider.ErrAlreadyExist +} + +// Delete deletes the node group on the cloud provider side. +// This will be executed only for autoprovisioned node groups, once their size drops to 0. +func (as *AgentPool) Delete() error { + return cloudprovider.ErrNotImplemented +} + +// Autoprovisioned returns true if the node group is autoprovisioned. +func (as *AgentPool) Autoprovisioned() bool { + return false +} + +// MaxSize returns maximum size of the node group. +func (as *AgentPool) MaxSize() int { + return as.maxSize +} + +// GetVMIndexes gets indexes of all virtual machines belongting to the agent pool. +func (as *AgentPool) GetVMIndexes() ([]int, map[int]VirtualMachineID, error) { + instances, err := as.GetVirtualMachines() + if err != nil { + return nil, nil, err + } + + indexes := make([]int, 0) + indexToVM := make(map[int]VirtualMachineID) + for _, instance := range instances { + index, err := GetVMNameIndex(instance.StorageProfile.OsDisk.OsType, *instance.Name) + if err != nil { + return nil, nil, err + } + + indexes = append(indexes, index) + indexToVM[index] = VirtualMachineID{ + ID: "azure://" + strings.ToLower(*instance.ID), + VMID: "azure://" + strings.ToLower(*instance.VMID), + } + } + + sortedIndexes := sort.IntSlice(indexes) + sortedIndexes.Sort() + return sortedIndexes, indexToVM, nil +} + +// TargetSize returns the current TARGET size of the node group. It is possible that the +// number is different from the number of nodes registered in Kubernetes. +func (as *AgentPool) TargetSize() (int, error) { + as.mutex.Lock() + defer as.mutex.Unlock() + + if as.targetSize != -1 { + return as.targetSize, nil + } + + indexes, _, err := as.GetVMIndexes() + if err != nil { + return 0, err + } + + as.targetSize = len(indexes) + return as.targetSize, nil +} + +// IncreaseSize increases agent pool size +func (as *AgentPool) IncreaseSize(delta int) error { + as.mutex.Lock() + defer as.mutex.Unlock() + + if delta <= 0 { + return fmt.Errorf("size increase must be positive") + } + + indexes, _, err := as.GetVMIndexes() + if err != nil { + return err + } + + curSize := len(indexes) + if curSize+delta > as.MaxSize() { + return fmt.Errorf("size increase too large - desired:%d max:%d", curSize+delta, as.MaxSize()) + } + + highestUsedIndex := indexes[len(indexes)-1] + countForTemplate := curSize + delta + as.targetSize = countForTemplate + if highestUsedIndex != 0 { + countForTemplate += highestUsedIndex + 1 - curSize + } + as.parameters[as.Name+"Count"] = map[string]int{"value": countForTemplate} + as.parameters[as.Name+"Offset"] = map[string]int{"value": highestUsedIndex + 1} + + cancel := make(chan struct{}) + newDeploymentName := fmt.Sprintf("cluster-autoscaler-%d", rand.New(rand.NewSource(time.Now().UnixNano())).Int31()) + newDeployment := resources.Deployment{ + Properties: &resources.DeploymentProperties{ + Template: &as.template, + Parameters: &as.parameters, + Mode: resources.Incremental, + }, + } + _, errChan := as.deploymentsClient.CreateOrUpdate(as.config.ResourceGroup, newDeploymentName, newDeployment, cancel) + glog.V(3).Infof("Waiting for deploymentsClient.CreateOrUpdate(%s, %s, %s)", as.config.ResourceGroup, newDeploymentName, newDeployment) + return <-errChan +} + +// GetVirtualMachines returns list of nodes for the given agent pool. +func (as *AgentPool) GetVirtualMachines() (instances []compute.VirtualMachine, err error) { + result, err := as.virtualMachinesClient.List(as.config.ResourceGroup) + if err != nil { + return nil, err + } + + moreResult := (result.Value != nil && len(*result.Value) > 0) + for moreResult { + for _, instance := range *result.Value { + if instance.Tags == nil { + continue + } + + tags := *instance.Tags + vmPoolName := tags["poolName"] + if *vmPoolName != as.Id() { + continue + } + + instances = append(instances, instance) + } + + moreResult = false + if result.NextLink != nil { + result, err = as.virtualMachinesClient.ListNextResults(result) + if err != nil { + glog.Errorf("virtualMachinesClient.ListNextResults failed: %v", err) + return nil, err + } + + moreResult = (result.Value != nil && len(*result.Value) > 0) + } + } + + return instances, nil +} + +// DecreaseTargetSize decreases the target size of the node group. This function +// doesn't permit to delete any existing node and can be used only to reduce the +// request for new nodes that have not been yet fulfilled. Delta should be negative. +// It is assumed that cloud provider will not delete the existing nodes if the size +// when there is an option to just decrease the target. +func (as *AgentPool) DecreaseTargetSize(delta int) error { + as.mutex.Lock() + defer as.mutex.Unlock() + + nodes, err := as.GetVirtualMachines() + if err != nil { + return err + } + + curTargetSize := as.targetSize + if curTargetSize+delta < len(nodes) { + return fmt.Errorf("attempt to delete existing nodes targetSize:%d delta:%d existingNodes: %d", + curTargetSize, delta, len(nodes)) + } + + as.targetSize = curTargetSize + delta + return nil +} + +// Belongs returns true if the given node belongs to the NodeGroup. +func (as *AgentPool) Belongs(node *apiv1.Node) (bool, error) { + glog.V(6).Infof("Check if node belongs to this agent pool: AgentPool:%v, node:%v\n", as, node) + + ref := &AzureRef{ + Name: strings.ToLower(node.Spec.ProviderID), + } + + targetAsg, err := as.GetNodeGroupForInstance(ref) + if err != nil { + return false, err + } + if targetAsg == nil { + return false, fmt.Errorf("%s doesn't belong to a known agent pool", node.Name) + } + if targetAsg.Id() != as.Id() { + return false, nil + } + return true, nil +} + +// DeleteInstances deletes the given instances. All instances must be controlled by the same ASG. +func (as *AgentPool) DeleteInstances(instances []*AzureRef) error { + if len(instances) == 0 { + return nil + } + + commonAsg, err := as.GetNodeGroupForInstance(instances[0]) + if err != nil { + return err + } + + for _, instance := range instances { + asg, err := as.GetNodeGroupForInstance(instance) + if err != nil { + return err + } + + if asg != commonAsg { + return fmt.Errorf("cannot delete instance (%s) which don't belong to the same node pool (%q)", instance.GetKey(), commonAsg) + } + } + + for _, instance := range instances { + name, err := resourceName((*instance).Name) + if err != nil { + glog.Errorf("Get name for instance %q failed: %v", *instance, err) + return err + } + + err = as.deleteVirtualMachine(name) + if err != nil { + glog.Errorf("Delete virtual machine %q failed: %v", name, err) + return err + } + } + + return nil +} + +// DeleteNodes deletes the nodes from the group. +func (as *AgentPool) DeleteNodes(nodes []*apiv1.Node) error { + glog.V(8).Infof("Delete nodes requested: %v\n", nodes) + indexes, _, err := as.GetVMIndexes() + if err != nil { + return err + } + + if len(indexes) <= as.MinSize() { + return fmt.Errorf("min size reached, nodes will not be deleted") + } + + refs := make([]*AzureRef, 0, len(nodes)) + for _, node := range nodes { + belongs, err := as.Belongs(node) + if err != nil { + return err + } + + if belongs != true { + return fmt.Errorf("%s belongs to a different asg than %s", node.Name, as.Id()) + } + + azureRef := &AzureRef{ + Name: strings.ToLower(node.Spec.ProviderID), + } + refs = append(refs, azureRef) + } + + return as.DeleteInstances(refs) +} + +// Id returns AgentPool id. +func (as *AgentPool) Id() string { + return as.Name +} + +// Debug returns a debug string for the agent pool. +func (as *AgentPool) Debug() string { + return fmt.Sprintf("%s (%d:%d)", as.Id(), as.MinSize(), as.MaxSize()) +} + +// TemplateNodeInfo returns a node template for this agent pool. +func (as *AgentPool) TemplateNodeInfo() (*schedulercache.NodeInfo, error) { + return nil, cloudprovider.ErrNotImplemented +} + +// Nodes returns a list of all nodes that belong to this node group. +func (as *AgentPool) Nodes() ([]string, error) { + instances, err := as.GetVirtualMachines() + if err != nil { + return nil, err + } + + nodes := make([]string, 0, len(instances)) + for _, instance := range instances { + // Convert to lower because instance.ID is in different in different API calls (e.g. GET and LIST). + name := "azure://" + strings.ToLower(*instance.ID) + nodes = append(nodes, name) + } + + return nodes, nil +} + +func (as *AgentPool) deleteBlob(accountName, vhdContainer, vhdBlob string) error { + storageKeysResult, err := as.storageAccountsClient.ListKeys(as.config.ResourceGroup, accountName) + if err != nil { + return err + } + + keys := *storageKeysResult.Keys + client, err := azStorage.NewBasicClientOnSovereignCloud(accountName, to.String(keys[0].Value), as.env) + if err != nil { + return err + } + + bs := client.GetBlobService() + containerRef := bs.GetContainerReference(vhdContainer) + blobRef := containerRef.GetBlobReference(vhdBlob) + + return blobRef.Delete(&azStorage.DeleteBlobOptions{}) +} + +// deleteVirtualMachine deletes a VM and any associated OS disk +func (as *AgentPool) deleteVirtualMachine(name string) error { + vm, err := as.virtualMachinesClient.Get(as.config.ResourceGroup, name, "") + if err != nil { + glog.Errorf("failed to get VM: %s/%s: %s", as.config.ResourceGroup, name, err.Error()) + return err + } + + vhd := vm.VirtualMachineProperties.StorageProfile.OsDisk.Vhd + managedDisk := vm.VirtualMachineProperties.StorageProfile.OsDisk.ManagedDisk + if vhd == nil && managedDisk == nil { + glog.Errorf("failed to get a valid os disk URI for VM: %s/%s", as.config.ResourceGroup, name) + return fmt.Errorf("os disk does not have a VHD URI") + } + + osDiskName := vm.VirtualMachineProperties.StorageProfile.OsDisk.Name + var nicName string + nicID := (*vm.VirtualMachineProperties.NetworkProfile.NetworkInterfaces)[0].ID + if nicID == nil { + glog.Warningf("NIC ID is not set for VM (%s/%s)", as.config.ResourceGroup, name) + } else { + nicName, err = resourceName(*nicID) + if err != nil { + return err + } + glog.Infof("found nic name for VM (%s/%s): %s", as.config.ResourceGroup, name, nicName) + } + glog.Infof("deleting VM: %s/%s", as.config.ResourceGroup, name) + _, deleteErrChan := as.virtualMachinesClient.Delete(as.config.ResourceGroup, name, nil) + glog.Infof("waiting for vm deletion: %s/%s", as.config.ResourceGroup, name) + if err := <-deleteErrChan; err != nil { + return err + } + + if len(nicName) > 0 { + glog.Infof("deleting nic: %s/%s", as.config.ResourceGroup, nicName) + _, nicErrChan := as.interfacesClient.Delete(as.config.ResourceGroup, nicName, nil) + glog.Infof("waiting for nic deletion: %s/%s", as.config.ResourceGroup, nicName) + if nicErr := <-nicErrChan; nicErr != nil { + return nicErr + } + } + + if vhd != nil { + accountName, vhdContainer, vhdBlob, err := splitBlobURI(*vhd.URI) + if err != nil { + return err + } + + glog.Infof("found os disk storage reference: %s %s %s", accountName, vhdContainer, vhdBlob) + + glog.Infof("deleting blob: %s/%s", vhdContainer, vhdBlob) + if err = as.deleteBlob(accountName, vhdContainer, vhdBlob); err != nil { + return err + } + } else if managedDisk != nil { + if osDiskName == nil { + glog.Warningf("osDisk is not set for VM %s/%s", as.config.ResourceGroup, name) + } else { + glog.Infof("deleting managed disk: %s/%s", as.config.ResourceGroup, *osDiskName) + _, diskErrChan := as.disksClient.Delete(as.config.ResourceGroup, *osDiskName, nil) + + if err := <-diskErrChan; err != nil { + return err + } + } + } + + return nil +} diff --git a/cluster-autoscaler/cloudprovider/azure/azure_cloud_provider.go b/cluster-autoscaler/cloudprovider/azure/azure_cloud_provider.go index d8b3228d5845..1112a51399e6 100644 --- a/cluster-autoscaler/cloudprovider/azure/azure_cloud_provider.go +++ b/cluster-autoscaler/cloudprovider/azure/azure_cloud_provider.go @@ -27,7 +27,6 @@ import ( "k8s.io/apimachinery/pkg/api/resource" "k8s.io/autoscaler/cluster-autoscaler/cloudprovider" "k8s.io/autoscaler/cluster-autoscaler/utils/errors" - "k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache" ) const ( @@ -38,7 +37,7 @@ const ( // AzureCloudProvider provides implementation of CloudProvider interface for Azure. type AzureCloudProvider struct { azureManager *AzureManager - scaleSets []*ScaleSet + nodeGroups []cloudprovider.NodeGroup resourceLimiter *cloudprovider.ResourceLimiter } @@ -66,12 +65,13 @@ func (azure *AzureCloudProvider) Cleanup() error { // addNodeGroup adds node group defined in string spec. Format: // minNodes:maxNodes:scaleSetName func (azure *AzureCloudProvider) addNodeGroup(spec string) error { - scaleSet, err := buildScaleSet(spec, azure.azureManager) + nodeGroup, err := azure.buildNodeGroup(spec) if err != nil { return err } - azure.scaleSets = append(azure.scaleSets, scaleSet) - azure.azureManager.RegisterScaleSet(scaleSet) + + azure.nodeGroups = append(azure.nodeGroups, nodeGroup) + azure.azureManager.RegisterNodeGroup(nodeGroup) return nil } @@ -82,9 +82,9 @@ func (azure *AzureCloudProvider) Name() string { // NodeGroups returns all node groups configured for this cloud provider. func (azure *AzureCloudProvider) NodeGroups() []cloudprovider.NodeGroup { - result := make([]cloudprovider.NodeGroup, 0, len(azure.scaleSets)) - for _, scaleSet := range azure.scaleSets { - result = append(result, scaleSet) + result := make([]cloudprovider.NodeGroup, 0, len(azure.nodeGroups)) + for _, nodeGroup := range azure.nodeGroups { + result = append(result, nodeGroup) } return result } @@ -96,9 +96,7 @@ func (azure *AzureCloudProvider) NodeGroupForNode(node *apiv1.Node) (cloudprovid Name: strings.ToLower(node.Spec.ProviderID), } - scaleSet, err := azure.azureManager.GetScaleSetForInstance(ref) - - return scaleSet, err + return azure.azureManager.GetNodeGroupForInstance(ref) } // Pricing returns pricing model for this cloud provider or error if not available. @@ -128,203 +126,31 @@ func (azure *AzureCloudProvider) Refresh() error { return nil } -// AzureRef contains a reference to some entity in Azure world. -type AzureRef struct { - Name string -} - -// GetKey returns key of the given azure reference. -func (m *AzureRef) GetKey() string { - return m.Name -} - -// AzureRefFromProviderId creates InstanceConfig object from provider id which -// must be in format: azure:///resourceGroupName/name -func AzureRefFromProviderId(id string) (*AzureRef, error) { - splitted := strings.Split(id[9:], "/") - if len(splitted) != 2 { - return nil, fmt.Errorf("Wrong id: expected format azure:///, got %v", id) - } - return &AzureRef{ - Name: splitted[len(splitted)-1], - }, nil -} - -// ScaleSet implements NodeGroup interface. -type ScaleSet struct { - AzureRef - - azureManager *AzureManager - minSize int - maxSize int -} - -// MinSize returns minimum size of the node group. -func (scaleSet *ScaleSet) MinSize() int { - return scaleSet.minSize -} - -// Exist checks if the node group really exists on the cloud provider side. Allows to tell the -// theoretical node group from the real one. -func (scaleSet *ScaleSet) Exist() bool { - return true -} - -// Create creates the node group on the cloud provider side. -func (scaleSet *ScaleSet) Create() error { - return cloudprovider.ErrAlreadyExist -} - -// Delete deletes the node group on the cloud provider side. -// This will be executed only for autoprovisioned node groups, once their size drops to 0. -func (scaleSet *ScaleSet) Delete() error { - return cloudprovider.ErrNotImplemented -} - -// Autoprovisioned returns true if the node group is autoprovisioned. -func (scaleSet *ScaleSet) Autoprovisioned() bool { - return false -} - -// MaxSize returns maximum size of the node group. -func (scaleSet *ScaleSet) MaxSize() int { - return scaleSet.maxSize -} - -// TargetSize returns the current TARGET size of the node group. It is possible that the -// number is different from the number of nodes registered in Kubernetes. -func (scaleSet *ScaleSet) TargetSize() (int, error) { - size, err := scaleSet.azureManager.GetScaleSetSize(scaleSet) - return int(size), err -} - -// IncreaseSize increases Scale Set size -func (scaleSet *ScaleSet) IncreaseSize(delta int) error { - if delta <= 0 { - return fmt.Errorf("size increase must be positive") - } - size, err := scaleSet.azureManager.GetScaleSetSize(scaleSet) - if err != nil { - return err - } - if int(size)+delta > scaleSet.MaxSize() { - return fmt.Errorf("size increase too large - desired:%d max:%d", int(size)+delta, scaleSet.MaxSize()) - } - return scaleSet.azureManager.SetScaleSetSize(scaleSet, size+int64(delta)) -} - -// DecreaseTargetSize decreases the target size of the node group. This function -// doesn't permit to delete any existing node and can be used only to reduce the -// request for new nodes that have not been yet fulfilled. Delta should be negative. -// It is assumed that cloud provider will not delete the existing nodes if the size -// when there is an option to just decrease the target. -func (scaleSet *ScaleSet) DecreaseTargetSize(delta int) error { - if delta >= 0 { - return fmt.Errorf("size decrease size must be negative") - } - size, err := scaleSet.azureManager.GetScaleSetSize(scaleSet) - if err != nil { - return err - } - nodes, err := scaleSet.azureManager.GetScaleSetVms(scaleSet) - if err != nil { - return err - } - if int(size)+delta < len(nodes) { - return fmt.Errorf("attempt to delete existing nodes targetSize:%d delta:%d existingNodes: %d", - size, delta, len(nodes)) - } - return scaleSet.azureManager.SetScaleSetSize(scaleSet, size+int64(delta)) -} - -// Belongs returns true if the given node belongs to the NodeGroup. -func (scaleSet *ScaleSet) Belongs(node *apiv1.Node) (bool, error) { - glog.V(6).Infof("Check if node belongs to this scale set: scaleset:%v, node:%v\n", scaleSet, node) - - ref := &AzureRef{ - Name: strings.ToLower(node.Spec.ProviderID), - } - - targetAsg, err := scaleSet.azureManager.GetScaleSetForInstance(ref) - if err != nil { - return false, err - } - if targetAsg == nil { - return false, fmt.Errorf("%s doesn't belong to a known scale set", node.Name) - } - if targetAsg.Id() != scaleSet.Id() { - return false, nil - } - return true, nil -} - -// DeleteNodes deletes the nodes from the group. -func (scaleSet *ScaleSet) DeleteNodes(nodes []*apiv1.Node) error { - glog.V(8).Infof("Delete nodes requested: %v\n", nodes) - size, err := scaleSet.azureManager.GetScaleSetSize(scaleSet) - if err != nil { - return err - } - if int(size) <= scaleSet.MinSize() { - return fmt.Errorf("min size reached, nodes will not be deleted") - } - refs := make([]*AzureRef, 0, len(nodes)) - for _, node := range nodes { - belongs, err := scaleSet.Belongs(node) - if err != nil { - return err - } - if belongs != true { - return fmt.Errorf("%s belongs to a different asg than %s", node.Name, scaleSet.Id()) - } - azureRef := &AzureRef{ - Name: strings.ToLower(node.Spec.ProviderID), - } - refs = append(refs, azureRef) - } - return scaleSet.azureManager.DeleteInstances(refs) -} - -// Id returns ScaleSet id. -func (scaleSet *ScaleSet) Id() string { - return scaleSet.Name -} - -// Debug returns a debug string for the Scale Set. -func (scaleSet *ScaleSet) Debug() string { - return fmt.Sprintf("%s (%d:%d)", scaleSet.Id(), scaleSet.MinSize(), scaleSet.MaxSize()) -} - -// TemplateNodeInfo returns a node template for this scale set. -func (scaleSet *ScaleSet) TemplateNodeInfo() (*schedulercache.NodeInfo, error) { - return nil, cloudprovider.ErrNotImplemented -} - -// Create ScaleSet from provided spec. +// Create nodeGroup from provided spec. // spec is in the following format: min-size:max-size:scale-set-name. -func buildScaleSet(spec string, azureManager *AzureManager) (*ScaleSet, error) { +func (azure *AzureCloudProvider) buildNodeGroup(spec string) (cloudprovider.NodeGroup, error) { tokens := strings.SplitN(spec, ":", 3) if len(tokens) != 3 { return nil, fmt.Errorf("wrong nodes configuration: %s", spec) } - scaleSet := ScaleSet{ - azureManager: azureManager, - } + minSize := 0 + maxSize := 0 + name := tokens[2] if size, err := strconv.Atoi(tokens[0]); err == nil { if size <= 0 { return nil, fmt.Errorf("min size must be >= 1, got: %d", size) } - scaleSet.minSize = size + minSize = size } else { return nil, fmt.Errorf("failed to set min size: %s, expected integer", tokens[0]) } if size, err := strconv.Atoi(tokens[1]); err == nil { - if size < scaleSet.minSize { + if size < minSize { return nil, fmt.Errorf("max size must be greater or equal to min size") } - scaleSet.maxSize = size + maxSize = size } else { return nil, fmt.Errorf("failed to set max size: %s, expected integer", tokens[1]) } @@ -333,11 +159,19 @@ func buildScaleSet(spec string, azureManager *AzureManager) (*ScaleSet, error) { return nil, fmt.Errorf("scale set name must not be blank, got spec: %s", spec) } - scaleSet.Name = tokens[2] - return &scaleSet, nil + if azure.azureManager.config.VMType == vmTypeStandard { + return NewAgentPool(name, minSize, maxSize, azure.azureManager) + } + + return NewScaleSet(name, minSize, maxSize, azure.azureManager) } -// Nodes returns a list of all nodes that belong to this node group. -func (scaleSet *ScaleSet) Nodes() ([]string, error) { - return scaleSet.azureManager.GetScaleSetVms(scaleSet) +// AzureRef contains a reference to some entity in Azure world. +type AzureRef struct { + Name string +} + +// GetKey returns key of the given azure reference. +func (m *AzureRef) GetKey() string { + return m.Name } diff --git a/cluster-autoscaler/cloudprovider/azure/azure_cloud_provider_test.go b/cluster-autoscaler/cloudprovider/azure/azure_cloud_provider_test.go index 9dba0b2e2e1f..31700b62e541 100644 --- a/cluster-autoscaler/cloudprovider/azure/azure_cloud_provider_test.go +++ b/cluster-autoscaler/cloudprovider/azure/azure_cloud_provider_test.go @@ -17,100 +17,47 @@ limitations under the License. package azure import ( - "net/http" "testing" - "github.com/Azure/azure-sdk-for-go/arm/compute" - "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/azure" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" apiv1 "k8s.io/api/core/v1" "k8s.io/autoscaler/cluster-autoscaler/cloudprovider" ) -// Mock for VirtualMachineScaleSetsClient -type VirtualMachineScaleSetsClientMock struct { - mock.Mock -} - -func (client *VirtualMachineScaleSetsClientMock) Get(resourceGroupName string, - vmScaleSetName string) (result compute.VirtualMachineScaleSet, err error) { - capacity := int64(2) - properties := compute.VirtualMachineScaleSetProperties{} - return compute.VirtualMachineScaleSet{ - Name: &vmScaleSetName, - Sku: &compute.Sku{ - Capacity: &capacity, - }, - VirtualMachineScaleSetProperties: &properties, - }, nil -} - -func (client *VirtualMachineScaleSetsClientMock) CreateOrUpdate( - resourceGroupName string, vmScaleSetName string, parameters compute.VirtualMachineScaleSet, cancel <-chan struct{}) (<-chan compute.VirtualMachineScaleSet, <-chan error) { - errChan := make(chan error) - go func() { - errChan <- nil - }() - return nil, errChan -} - -func (client *VirtualMachineScaleSetsClientMock) DeleteInstances(resourceGroupName string, vmScaleSetName string, - vmInstanceIDs compute.VirtualMachineScaleSetVMInstanceRequiredIDs, cancel <-chan struct{}) (<-chan compute.OperationStatusResponse, <-chan error) { - args := client.Called(resourceGroupName, vmScaleSetName, vmInstanceIDs, cancel) - errChan := make(chan error) - go func() { - errChan <- args.Error(1) - }() - return nil, errChan -} - -// Mock for VirtualMachineScaleSetVMsClient -type VirtualMachineScaleSetVMsClientMock struct { - mock.Mock -} - -func (m *VirtualMachineScaleSetVMsClientMock) List(resourceGroupName string, virtualMachineScaleSetName string, filter string, selectParameter string, expand string) (result compute.VirtualMachineScaleSetVMListResult, err error) { - value := make([]compute.VirtualMachineScaleSetVM, 1) - vmInstanceID := "test-instance-id" - properties := compute.VirtualMachineScaleSetVMProperties{} - vmID := "123E4567-E89B-12D3-A456-426655440000" - properties.VMID = &vmID - value[0] = compute.VirtualMachineScaleSetVM{ - ID: &vmID, - InstanceID: &vmInstanceID, - VirtualMachineScaleSetVMProperties: &properties, +func newTestAzureManager() *AzureManager { + return &AzureManager{ + config: &Config{VMType: vmTypeVMSS}, + env: azure.PublicCloud, + interrupt: make(chan struct{}), + instanceIDsCache: make(map[string]string), + nodeGroups: make([]cloudprovider.NodeGroup, 0), + nodeGroupsCache: make(map[AzureRef]cloudprovider.NodeGroup), + + disksClient: &DisksClientMock{}, + interfacesClient: &InterfacesClientMock{}, + storageAccountsClient: &AccountsClientMock{}, + deploymentsClient: &DeploymentsClientMock{}, + virtualMachinesClient: &VirtualMachinesClientMock{}, + virtualMachineScaleSetsClient: &VirtualMachineScaleSetsClientMock{}, + virtualMachineScaleSetVMsClient: &VirtualMachineScaleSetVMsClientMock{}, } - - return compute.VirtualMachineScaleSetVMListResult{ - Value: &value, - }, nil - } -var testAzureManager = &AzureManager{ - scaleSets: make([]*scaleSetInformation, 0), - scaleSetClient: &VirtualMachineScaleSetsClientMock{}, - scaleSetVmClient: &VirtualMachineScaleSetVMsClientMock{}, - scaleSetCache: make(map[AzureRef]*ScaleSet), - interrupt: make(chan struct{}), -} - -func testProvider(t *testing.T, m *AzureManager) *AzureCloudProvider { +func newTestProvider() (*AzureCloudProvider, error) { + manager := newTestAzureManager() resourceLimiter := cloudprovider.NewResourceLimiter( map[string]int64{cloudprovider.ResourceNameCores: 1, cloudprovider.ResourceNameMemory: 10000000}, map[string]int64{cloudprovider.ResourceNameCores: 10, cloudprovider.ResourceNameMemory: 100000000}) - provider, err := BuildAzureCloudProvider(m, nil, resourceLimiter) - assert.NoError(t, err) - return provider + return BuildAzureCloudProvider(manager, nil, resourceLimiter) } func TestBuildAzureCloudProvider(t *testing.T) { resourceLimiter := cloudprovider.NewResourceLimiter( map[string]int64{cloudprovider.ResourceNameCores: 1, cloudprovider.ResourceNameMemory: 10000000}, map[string]int64{cloudprovider.ResourceNameCores: 10, cloudprovider.ResourceNameMemory: 100000000}) - m := testAzureManager + m := newTestAzureManager() _, err := BuildAzureCloudProvider(m, []string{"bad spec"}, resourceLimiter) assert.Error(t, err) @@ -119,56 +66,50 @@ func TestBuildAzureCloudProvider(t *testing.T) { } func TestAddNodeGroup(t *testing.T) { - provider := testProvider(t, testAzureManager) - err := provider.addNodeGroup("bad spec") + provider, err := newTestProvider() + assert.NoError(t, err) + + err = provider.addNodeGroup("bad spec") assert.Error(t, err) - assert.Equal(t, len(provider.scaleSets), 0) + assert.Equal(t, len(provider.nodeGroups), 0) err = provider.addNodeGroup("1:5:test-asg") assert.NoError(t, err) - assert.Equal(t, len(provider.scaleSets), 1) + assert.Equal(t, len(provider.nodeGroups), 1) } func TestName(t *testing.T) { - provider := testProvider(t, testAzureManager) + provider, err := newTestProvider() + assert.NoError(t, err) assert.Equal(t, provider.Name(), "azure") } func TestNodeGroups(t *testing.T) { - provider := testProvider(t, testAzureManager) + provider, err := newTestProvider() + assert.NoError(t, err) assert.Equal(t, len(provider.NodeGroups()), 0) - err := provider.addNodeGroup("1:5:test-asg") + + err = provider.addNodeGroup("1:5:test-asg") assert.NoError(t, err) assert.Equal(t, len(provider.NodeGroups()), 1) } func TestNodeGroupForNode(t *testing.T) { + provider, err := newTestProvider() + assert.NoError(t, err) + + err = provider.addNodeGroup("1:5:test-asg") + assert.NoError(t, err) + assert.Equal(t, len(provider.nodeGroups), 1) + node := &apiv1.Node{ Spec: apiv1.NodeSpec{ ProviderID: "azure://123E4567-E89B-12D3-A456-426655440000", }, } - - scaleSetVmClient := VirtualMachineScaleSetVMsClientMock{} - - var testAzureManager = &AzureManager{ - scaleSets: make([]*scaleSetInformation, 0), - scaleSetClient: &VirtualMachineScaleSetsClientMock{}, - scaleSetVmClient: &scaleSetVmClient, - scaleSetCache: make(map[AzureRef]*ScaleSet), - } - - provider := testProvider(t, testAzureManager) - err := provider.addNodeGroup("1:5:test-asg") - assert.NoError(t, err) - - assert.Equal(t, len(provider.scaleSets), 1) - group, err := provider.NodeGroupForNode(node) - assert.NoError(t, err) assert.NotNil(t, group, "Group should not be nil") - assert.Equal(t, group.Id(), "test-asg") assert.Equal(t, group.MinSize(), 1) assert.Equal(t, group.MaxSize(), 5) @@ -179,173 +120,36 @@ func TestNodeGroupForNode(t *testing.T) { ProviderID: "azure:///subscriptions/subscripion/resourceGroups/test-resource-group/providers/Microsoft.Compute/virtualMachines/test-instance-id-not-in-group", }, } - group, err = provider.NodeGroupForNode(nodeNotInGroup) assert.NoError(t, err) assert.Nil(t, group) - -} - -func TestAzureRefFromProviderId(t *testing.T) { - _, err := AzureRefFromProviderId("azure:///123") - assert.Error(t, err) - _, err = AzureRefFromProviderId("azure://test/rg/test-instance-id") - assert.Error(t, err) - - // Example id: "azure:///subscriptions/subscriptionId/resourceGroups/kubernetes/providers/Microsoft.Compute/virtualMachines/kubernetes-master" - azureRef, err := AzureRefFromProviderId("azure:////kubernetes-master") - assert.NoError(t, err) - assert.Equal(t, &AzureRef{ - Name: "kubernetes-master", - }, azureRef) -} - -func TestMaxSize(t *testing.T) { - provider := testProvider(t, testAzureManager) - err := provider.addNodeGroup("1:5:test-asg") - assert.NoError(t, err) - assert.Equal(t, len(provider.scaleSets), 1) - assert.Equal(t, provider.scaleSets[0].MaxSize(), 5) -} - -func TestMinSize(t *testing.T) { - provider := testProvider(t, testAzureManager) - err := provider.addNodeGroup("1:5:test-asg") - assert.NoError(t, err) - assert.Equal(t, len(provider.scaleSets), 1) - assert.Equal(t, provider.scaleSets[0].MinSize(), 1) } -func TestTargetSize(t *testing.T) { - provider := testProvider(t, testAzureManager) - err := provider.addNodeGroup("1:5:test-asg") - assert.NoError(t, err) - targetSize, err := provider.scaleSets[0].TargetSize() - assert.Equal(t, targetSize, 2) +func TestBuildNodeGroup(t *testing.T) { + provider, err := newTestProvider() assert.NoError(t, err) -} - -func TestIncreaseSize(t *testing.T) { - var testAzureManager = &AzureManager{ - scaleSets: make([]*scaleSetInformation, 0), - scaleSetClient: &VirtualMachineScaleSetsClientMock{}, - scaleSetVmClient: &VirtualMachineScaleSetVMsClientMock{}, - scaleSetCache: make(map[AzureRef]*ScaleSet), - } - - provider := testProvider(t, testAzureManager) - - err := provider.addNodeGroup("1:5:test-asg") - assert.NoError(t, err) - assert.Equal(t, len(provider.scaleSets), 1) - - err = provider.scaleSets[0].IncreaseSize(1) - assert.NoError(t, err) -} - -func TestBelongs(t *testing.T) { - var testAzureManager = &AzureManager{ - scaleSets: make([]*scaleSetInformation, 0), - scaleSetClient: &VirtualMachineScaleSetsClientMock{}, - scaleSetVmClient: &VirtualMachineScaleSetVMsClientMock{}, - scaleSetCache: make(map[AzureRef]*ScaleSet), - } - - provider := testProvider(t, testAzureManager) - err := provider.addNodeGroup("1:5:test-asg") - assert.NoError(t, err) - - invalidNode := &apiv1.Node{ - Spec: apiv1.NodeSpec{ - ProviderID: "azure:///subscriptions/subscriptionId/resourceGroups/kubernetes/providers/Microsoft.Compute/virtualMachines/invalid-instance-id", - }, - } - - _, err = provider.scaleSets[0].Belongs(invalidNode) - assert.Error(t, err) - - validNode := &apiv1.Node{ - Spec: apiv1.NodeSpec{ - ProviderID: "azure://123E4567-E89B-12D3-A456-426655440000", - }, - } - belongs, err := provider.scaleSets[0].Belongs(validNode) - assert.Equal(t, belongs, true) - assert.NoError(t, err) -} - -func TestDeleteNodes(t *testing.T) { - scaleSetClient := &VirtualMachineScaleSetsClientMock{} - m := &AzureManager{ - scaleSets: make([]*scaleSetInformation, 0), - scaleSetClient: scaleSetClient, - scaleSetVmClient: &VirtualMachineScaleSetVMsClientMock{}, - scaleSetCache: make(map[AzureRef]*ScaleSet), - } - - instanceIds := make([]string, 1) - instanceIds[0] = "test-instance-id" - response := autorest.Response{ - Response: &http.Response{ - Status: "OK", - }, - } - scaleSetClient.On("DeleteInstances", mock.Anything, "test-asg", mock.Anything, mock.Anything).Return(response, nil) - - provider := testProvider(t, m) - err := provider.addNodeGroup("1:5:test-asg") - assert.NoError(t, err) - - node := &apiv1.Node{ - Spec: apiv1.NodeSpec{ - ProviderID: "azure://123E4567-E89B-12D3-A456-426655440000", - }, - } - err = provider.scaleSets[0].DeleteNodes([]*apiv1.Node{node}) - assert.NoError(t, err) - scaleSetClient.AssertNumberOfCalls(t, "DeleteInstances", 1) -} - -func TestId(t *testing.T) { - provider := testProvider(t, testAzureManager) - err := provider.addNodeGroup("1:5:test-asg") - assert.NoError(t, err) - assert.Equal(t, len(provider.scaleSets), 1) - assert.Equal(t, provider.scaleSets[0].Id(), "test-asg") -} - -func TestDebug(t *testing.T) { - asg := ScaleSet{ - azureManager: testAzureManager, - minSize: 5, - maxSize: 55, - } - asg.Name = "test-scale-set" - assert.Equal(t, asg.Debug(), "test-scale-set (5:55)") -} -func TestBuildAsg(t *testing.T) { - _, err := buildScaleSet("a", nil) + _, err = provider.buildNodeGroup("a") assert.Error(t, err) - _, err = buildScaleSet("a:b:c", nil) + _, err = provider.buildNodeGroup("a:b:c") assert.Error(t, err) - _, err = buildScaleSet("1:", nil) + _, err = provider.buildNodeGroup("1:") assert.Error(t, err) - _, err = buildScaleSet("1:2:", nil) + _, err = provider.buildNodeGroup("1:2:") assert.Error(t, err) - _, err = buildScaleSet("-1:2:", nil) + _, err = provider.buildNodeGroup("-1:2:") assert.Error(t, err) - _, err = buildScaleSet("5:3:", nil) + _, err = provider.buildNodeGroup("5:3:") assert.Error(t, err) - _, err = buildScaleSet("5:ddd:test-name", nil) + _, err = provider.buildNodeGroup("5:ddd:test-name") assert.Error(t, err) - asg, err := buildScaleSet("111:222:test-name", nil) + asg, err := provider.buildNodeGroup("111:222:test-name") assert.NoError(t, err) assert.Equal(t, 111, asg.MinSize()) assert.Equal(t, 222, asg.MaxSize()) - assert.Equal(t, "test-name", asg.Name) + assert.Equal(t, "test-name", asg.Id()) } diff --git a/cluster-autoscaler/cloudprovider/azure/azure_fakes.go b/cluster-autoscaler/cloudprovider/azure/azure_fakes.go new file mode 100644 index 000000000000..7f418e2742b7 --- /dev/null +++ b/cluster-autoscaler/cloudprovider/azure/azure_fakes.go @@ -0,0 +1,284 @@ +/* +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 azure + +import ( + "fmt" + "net/http" + "sync" + + "github.com/Azure/azure-sdk-for-go/arm/compute" + "github.com/Azure/azure-sdk-for-go/arm/disk" + "github.com/Azure/azure-sdk-for-go/arm/resources/resources" + "github.com/Azure/azure-sdk-for-go/arm/storage" + "github.com/Azure/go-autorest/autorest" + "github.com/stretchr/testify/mock" +) + +// VirtualMachineScaleSetsClientMock mocks for VirtualMachineScaleSetsClient. +type VirtualMachineScaleSetsClientMock struct { + mock.Mock +} + +// Get gets the VirtualMachineScaleSet by vmScaleSetName. +func (client *VirtualMachineScaleSetsClientMock) Get(resourceGroupName string, + vmScaleSetName string) (result compute.VirtualMachineScaleSet, err error) { + capacity := int64(2) + properties := compute.VirtualMachineScaleSetProperties{} + return compute.VirtualMachineScaleSet{ + Name: &vmScaleSetName, + Sku: &compute.Sku{ + Capacity: &capacity, + }, + VirtualMachineScaleSetProperties: &properties, + }, nil +} + +// CreateOrUpdate creates or updates the VirtualMachineScaleSet. +func (client *VirtualMachineScaleSetsClientMock) CreateOrUpdate( + resourceGroupName string, vmScaleSetName string, parameters compute.VirtualMachineScaleSet, cancel <-chan struct{}) (<-chan compute.VirtualMachineScaleSet, <-chan error) { + errChan := make(chan error) + go func() { + errChan <- nil + }() + return nil, errChan +} + +// DeleteInstances deletes a set of instances for specified VirtualMachineScaleSet. +func (client *VirtualMachineScaleSetsClientMock) DeleteInstances(resourceGroupName string, vmScaleSetName string, + vmInstanceIDs compute.VirtualMachineScaleSetVMInstanceRequiredIDs, cancel <-chan struct{}) (<-chan compute.OperationStatusResponse, <-chan error) { + args := client.Called(resourceGroupName, vmScaleSetName, vmInstanceIDs, cancel) + errChan := make(chan error) + go func() { + errChan <- args.Error(1) + }() + return nil, errChan +} + +// VirtualMachineScaleSetVMsClientMock mocks for VirtualMachineScaleSetVMsClient. +type VirtualMachineScaleSetVMsClientMock struct { + mock.Mock +} + +// List gets a list of VirtualMachineScaleSetVMs. +func (m *VirtualMachineScaleSetVMsClientMock) List(resourceGroupName string, virtualMachineScaleSetName string, filter string, selectParameter string, expand string) (result compute.VirtualMachineScaleSetVMListResult, err error) { + value := make([]compute.VirtualMachineScaleSetVM, 1) + vmInstanceID := "test-instance-id" + properties := compute.VirtualMachineScaleSetVMProperties{} + vmID := "123E4567-E89B-12D3-A456-426655440000" + properties.VMID = &vmID + value[0] = compute.VirtualMachineScaleSetVM{ + ID: &vmID, + InstanceID: &vmInstanceID, + VirtualMachineScaleSetVMProperties: &properties, + } + + return compute.VirtualMachineScaleSetVMListResult{ + Value: &value, + }, nil +} + +// ListNextResults gets more results from previous VirtualMachineScaleSetVMListResult. +func (m *VirtualMachineScaleSetVMsClientMock) ListNextResults(lastResults compute.VirtualMachineScaleSetVMListResult) (result compute.VirtualMachineScaleSetVMListResult, err error) { + return result, nil +} + +// VirtualMachinesClientMock mocks for VirtualMachinesClient. +type VirtualMachinesClientMock struct { + mock.Mock + + mutex *sync.Mutex + FakeStore map[string]map[string]compute.VirtualMachine +} + +// CreateOrUpdate creates or updates the VirtualMachine. +func (m *VirtualMachinesClientMock) CreateOrUpdate(resourceGroupName string, VMName string, parameters compute.VirtualMachine, cancel <-chan struct{}) (<-chan compute.VirtualMachine, <-chan error) { + m.mutex.Lock() + defer m.mutex.Unlock() + resultChan := make(chan compute.VirtualMachine, 1) + errChan := make(chan error, 1) + var result compute.VirtualMachine + var err error + defer func() { + resultChan <- result + errChan <- err + close(resultChan) + close(errChan) + }() + if _, ok := m.FakeStore[resourceGroupName]; !ok { + m.FakeStore[resourceGroupName] = make(map[string]compute.VirtualMachine) + } + m.FakeStore[resourceGroupName][VMName] = parameters + result = m.FakeStore[resourceGroupName][VMName] + result.Response.Response = &http.Response{ + StatusCode: http.StatusOK, + } + err = nil + return resultChan, errChan +} + +// Get gets the VirtualMachine by VMName. +func (m *VirtualMachinesClientMock) Get(resourceGroupName string, VMName string, expand compute.InstanceViewTypes) (result compute.VirtualMachine, err error) { + m.mutex.Lock() + defer m.mutex.Unlock() + if _, ok := m.FakeStore[resourceGroupName]; ok { + if entity, ok := m.FakeStore[resourceGroupName][VMName]; ok { + return entity, nil + } + } + return result, autorest.DetailedError{ + StatusCode: http.StatusNotFound, + Message: "Not such VM", + } +} + +// List gets a lit of VirtualMachine inside the resource group. +func (m *VirtualMachinesClientMock) List(resourceGroupName string) (result compute.VirtualMachineListResult, err error) { + m.mutex.Lock() + defer m.mutex.Unlock() + + var value []compute.VirtualMachine + if _, ok := m.FakeStore[resourceGroupName]; ok { + for _, v := range m.FakeStore[resourceGroupName] { + value = append(value, v) + } + } + result.Response.Response = &http.Response{ + StatusCode: http.StatusOK, + } + result.NextLink = nil + result.Value = &value + + return result, nil +} + +// ListNextResults gets more results from previous VirtualMachineListResult. +func (m *VirtualMachinesClientMock) ListNextResults(lastResults compute.VirtualMachineListResult) (result compute.VirtualMachineListResult, err error) { + m.mutex.Lock() + defer m.mutex.Unlock() + return compute.VirtualMachineListResult{}, nil +} + +// Delete deletes the VirtualMachine by VMName. +func (m *VirtualMachinesClientMock) Delete(resourceGroupName string, VMName string, cancel <-chan struct{}) (<-chan compute.OperationStatusResponse, <-chan error) { + args := m.Called(resourceGroupName, VMName, cancel) + errChan := make(chan error) + go func() { + errChan <- args.Error(1) + }() + return nil, errChan +} + +// InterfacesClientMock mocks for InterfacesClient. +type InterfacesClientMock struct { + mock.Mock +} + +// Delete deletes the interface by networkInterfaceName. +func (m *InterfacesClientMock) Delete(resourceGroupName string, networkInterfaceName string, cancel <-chan struct{}) (<-chan autorest.Response, <-chan error) { + args := m.Called(resourceGroupName, networkInterfaceName, cancel) + errChan := make(chan error) + go func() { + errChan <- args.Error(1) + }() + return nil, errChan +} + +// DisksClientMock mocks for DisksClient. +type DisksClientMock struct { + mock.Mock +} + +// Delete deletes the disk by diskName. +func (m *DisksClientMock) Delete(resourceGroupName string, diskName string, cancel <-chan struct{}) (<-chan disk.OperationStatusResponse, <-chan error) { + args := m.Called(resourceGroupName, diskName, cancel) + errChan := make(chan error) + go func() { + errChan <- args.Error(1) + }() + return nil, errChan +} + +// AccountsClientMock mocks for AccountsClient. +type AccountsClientMock struct { + mock.Mock +} + +// ListKeys get a list of keys by accountName. +func (m *AccountsClientMock) ListKeys(resourceGroupName string, accountName string) (result storage.AccountListKeysResult, err error) { + args := m.Called(resourceGroupName, accountName) + return storage.AccountListKeysResult{}, args.Error(1) +} + +// DeploymentsClientMock mocks for DeploymentsClient. +type DeploymentsClientMock struct { + mock.Mock + + mutex *sync.Mutex + FakeStore map[string]resources.DeploymentExtended +} + +// Get gets the DeploymentExtended by deploymentName. +func (m *DeploymentsClientMock) Get(resourceGroupName string, deploymentName string) (result resources.DeploymentExtended, err error) { + m.mutex.Lock() + defer m.mutex.Unlock() + + deploy, ok := m.FakeStore[deploymentName] + if !ok { + return result, fmt.Errorf("deployment not found") + } + + return deploy, nil +} + +// ExportTemplate exports the deployment's template. +func (m *DeploymentsClientMock) ExportTemplate(resourceGroupName string, deploymentName string) (result resources.DeploymentExportResult, err error) { + m.mutex.Lock() + defer m.mutex.Unlock() + + deploy, ok := m.FakeStore[deploymentName] + if !ok { + return result, fmt.Errorf("deployment not found") + } + + return resources.DeploymentExportResult{ + Template: deploy.Properties.Template, + }, nil +} + +// CreateOrUpdate creates or updates the Deployment. +func (m *DeploymentsClientMock) CreateOrUpdate(resourceGroupName string, deploymentName string, parameters resources.Deployment, cancel <-chan struct{}) (<-chan resources.DeploymentExtended, <-chan error) { + m.mutex.Lock() + defer m.mutex.Unlock() + + errChan := make(chan error) + go func() { + errChan <- nil + }() + + deploy, ok := m.FakeStore[deploymentName] + if !ok { + deploy = resources.DeploymentExtended{ + Properties: &resources.DeploymentPropertiesExtended{}, + } + m.FakeStore[deploymentName] = deploy + } + + deploy.Properties.Parameters = parameters.Properties.Parameters + deploy.Properties.Template = parameters.Properties.Template + return nil, errChan +} diff --git a/cluster-autoscaler/cloudprovider/azure/azure_manager.go b/cluster-autoscaler/cloudprovider/azure/azure_manager.go index bf7b5584fdea..e183242245de 100644 --- a/cluster-autoscaler/cloudprovider/azure/azure_manager.go +++ b/cluster-autoscaler/cloudprovider/azure/azure_manager.go @@ -19,13 +19,18 @@ package azure import ( "fmt" "io" - "net/http" + "io/ioutil" "os" + "strconv" "strings" "sync" "time" "github.com/Azure/azure-sdk-for-go/arm/compute" + "github.com/Azure/azure-sdk-for-go/arm/disk" + "github.com/Azure/azure-sdk-for-go/arm/network" + "github.com/Azure/azure-sdk-for-go/arm/resources/resources" + "github.com/Azure/azure-sdk-for-go/arm/storage" "github.com/Azure/go-autorest/autorest" "github.com/Azure/go-autorest/autorest/adal" "github.com/Azure/go-autorest/autorest/azure" @@ -33,35 +38,75 @@ import ( "gopkg.in/gcfg.v1" "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/autoscaler/cluster-autoscaler/cloudprovider" ) -type scaleSetInformation struct { - config *ScaleSet - basename string -} +const ( + vmTypeVMSS = "vmss" + vmTypeStandard = "standard" +) -type scaleSetClient interface { +// VirtualMachineScaleSetsClient defines needed functions for azure compute.VirtualMachineScaleSetsClient. +type VirtualMachineScaleSetsClient interface { Get(resourceGroupName string, vmScaleSetName string) (result compute.VirtualMachineScaleSet, err error) CreateOrUpdate(resourceGroupName string, name string, parameters compute.VirtualMachineScaleSet, cancel <-chan struct{}) (<-chan compute.VirtualMachineScaleSet, <-chan error) DeleteInstances(resourceGroupName string, vmScaleSetName string, vmInstanceIDs compute.VirtualMachineScaleSetVMInstanceRequiredIDs, cancel <-chan struct{}) (<-chan compute.OperationStatusResponse, <-chan error) } -type scaleSetVMClient interface { +// VirtualMachineScaleSetVMsClient defines needed functions for azure compute.VirtualMachineScaleSetVMsClient. +type VirtualMachineScaleSetVMsClient interface { List(resourceGroupName string, virtualMachineScaleSetName string, filter string, selectParameter string, expand string) (result compute.VirtualMachineScaleSetVMListResult, err error) + ListNextResults(lastResults compute.VirtualMachineScaleSetVMListResult) (result compute.VirtualMachineScaleSetVMListResult, err error) } -// AzureManager handles Azure communication and data caching. -type AzureManager struct { - resourceGroupName string - subscription string - scaleSetClient scaleSetClient - scaleSetVmClient scaleSetVMClient +// VirtualMachinesClient defines needed functions for azure compute.VirtualMachinesClient. +type VirtualMachinesClient interface { + Get(resourceGroupName string, VMName string, expand compute.InstanceViewTypes) (result compute.VirtualMachine, err error) + Delete(resourceGroupName string, VMName string, cancel <-chan struct{}) (<-chan compute.OperationStatusResponse, <-chan error) + List(resourceGroupName string) (result compute.VirtualMachineListResult, err error) + ListNextResults(lastResults compute.VirtualMachineListResult) (result compute.VirtualMachineListResult, err error) +} + +// InterfacesClient defines needed functions for azure network.InterfacesClient. +type InterfacesClient interface { + Delete(resourceGroupName string, networkInterfaceName string, cancel <-chan struct{}) (<-chan autorest.Response, <-chan error) +} + +// DeploymentsClient defines needed functions for azure network.DeploymentsClient. +type DeploymentsClient interface { + Get(resourceGroupName string, deploymentName string) (result resources.DeploymentExtended, err error) + ExportTemplate(resourceGroupName string, deploymentName string) (result resources.DeploymentExportResult, err error) + CreateOrUpdate(resourceGroupName string, deploymentName string, parameters resources.Deployment, cancel <-chan struct{}) (<-chan resources.DeploymentExtended, <-chan error) +} - scaleSets []*scaleSetInformation - scaleSetCache map[AzureRef]*ScaleSet +// DisksClient defines needed functions for azure disk.DisksClient. +type DisksClient interface { + Delete(resourceGroupName string, diskName string, cancel <-chan struct{}) (<-chan disk.OperationStatusResponse, <-chan error) +} + +// AccountsClient defines needed functions for azure storage.AccountsClient. +type AccountsClient interface { + ListKeys(resourceGroupName string, accountName string) (result storage.AccountListKeysResult, err error) +} - // cache of mapping from instance id to the scale set id - scaleSetIdCache map[string]string +// AzureManager handles Azure communication and data caching. +type AzureManager struct { + config *Config + env azure.Environment + + virtualMachineScaleSetsClient VirtualMachineScaleSetsClient + virtualMachineScaleSetVMsClient VirtualMachineScaleSetVMsClient + virtualMachinesClient VirtualMachinesClient + deploymentsClient DeploymentsClient + interfacesClient InterfacesClient + disksClient DisksClient + storageAccountsClient AccountsClient + + nodeGroups []cloudprovider.NodeGroup + // cache of mapping from instance name to nodeGroup. + nodeGroupsCache map[AzureRef]cloudprovider.NodeGroup + // cache of mapping from instance name to instanceID. + instanceIDsCache map[string]string cacheMutex sync.Mutex interrupt chan struct{} @@ -69,102 +114,183 @@ type AzureManager struct { // Config holds the configuration parsed from the --cloud-config flag type Config struct { - Cloud string `json:"cloud" yaml:"cloud"` - TenantID string `json:"tenantId" yaml:"tenantId"` - SubscriptionID string `json:"subscriptionId" yaml:"subscriptionId"` - ResourceGroup string `json:"resourceGroup" yaml:"resourceGroup"` - Location string `json:"location" yaml:"location"` - VnetName string `json:"vnetName" yaml:"vnetName"` - SubnetName string `json:"subnetName" yaml:"subnetName"` - SecurityGroupName string `json:"securityGroupName" yaml:"securityGroupName"` - RouteTableName string `json:"routeTableName" yaml:"routeTableName"` - PrimaryAvailabilitySetName string `json:"primaryAvailabilitySetName" yaml:"primaryAvailabilitySetName"` - - AADClientID string `json:"aadClientId" yaml:"aadClientId"` - AADClientSecret string `json:"aadClientSecret" yaml:"aadClientSecret"` - AADTenantID string `json:"aadTenantId" yaml:"aadTenantId"` + Cloud string `json:"cloud" yaml:"cloud"` + TenantID string `json:"tenantId" yaml:"tenantId"` + SubscriptionID string `json:"subscriptionId" yaml:"subscriptionId"` + ResourceGroup string `json:"resourceGroup" yaml:"resourceGroup"` + VMType string `json:"vmType" yaml:"vmType"` + + AADClientID string `json:"aadClientId" yaml:"aadClientId"` + AADClientSecret string `json:"aadClientSecret" yaml:"aadClientSecret"` + AADClientCertPath string `json:"aadClientCertPath" yaml:"aadClientCertPath"` + AADClientCertPassword string `json:"aadClientCertPassword" yaml:"aadClientCertPassword"` + UseManagedIdentityExtension bool `json:"useManagedIdentityExtension" yaml:"useManagedIdentityExtension"` + + // Configs only for standard vmType (agent pools). + Deployment string `json:"deployment" yaml:"deployment"` + APIServerPrivateKey string `json:"apiServerPrivateKey" yaml:"apiServerPrivateKey"` + CAPrivateKey string `json:"caPrivateKey" yaml:"caPrivateKey"` + ClientPrivateKey string `json:"clientPrivateKey" yaml:"clientPrivateKey"` + KubeConfigPrivateKey string `json:"kubeConfigPrivateKey" yaml:"kubeConfigPrivateKey"` + WindowsAdminPassword string `json:"windowsAdminPassword" yaml:"windowsAdminPassword"` } // CreateAzureManager creates Azure Manager object to work with Azure. func CreateAzureManager(configReader io.Reader) (*AzureManager, error) { - subscriptionId := string("") - resourceGroup := string("") - tenantId := string("") - clientId := string("") - clientSecret := string("") - var scaleSetAPI scaleSetClient - var scaleSetVmAPI scaleSetVMClient + var err error + var cfg Config + if configReader != nil { - var cfg Config if err := gcfg.ReadInto(&cfg, configReader); err != nil { glog.Errorf("Couldn't read config: %v", err) return nil, err } - subscriptionId = cfg.SubscriptionID - resourceGroup = cfg.ResourceGroup - tenantId = cfg.AADTenantID - clientId = cfg.AADClientID - clientSecret = cfg.AADClientSecret - } else { - subscriptionId = os.Getenv("ARM_SUBSCRIPTION_ID") - resourceGroup = os.Getenv("ARM_RESOURCE_GROUP") - tenantId = os.Getenv("ARM_TENANT_ID") - clientId = os.Getenv("ARM_CLIENT_ID") - clientSecret = os.Getenv("ARM_CLIENT_SECRET") + cfg.Cloud = os.Getenv("ARM_CLOUD") + cfg.SubscriptionID = os.Getenv("ARM_SUBSCRIPTION_ID") + cfg.ResourceGroup = os.Getenv("ARM_RESOURCE_GROUP") + cfg.TenantID = os.Getenv("ARM_TENANT_ID") + cfg.AADClientID = os.Getenv("ARM_CLIENT_ID") + cfg.AADClientSecret = os.Getenv("ARM_CLIENT_SECRET") + cfg.VMType = strings.ToLower(os.Getenv("ARM_VM_TYPE")) + cfg.AADClientCertPath = os.Getenv("ARM_CLIENT_CERT_PATH") + cfg.AADClientCertPassword = os.Getenv("ARM_CLIENT_CERT_PASSWORD") + cfg.Deployment = os.Getenv("ARM_DEPLOYMENT") + cfg.APIServerPrivateKey = os.Getenv("ARM_APISEVER_PRIVATE_KEY") + cfg.CAPrivateKey = os.Getenv("ARM_CA_PRIVATE_KEY") + cfg.ClientPrivateKey = os.Getenv("ARM_CLIENT_PRIVATE_KEY") + cfg.KubeConfigPrivateKey = os.Getenv("ARM_KUBECONFIG_PRIVATE_KEY") + cfg.WindowsAdminPassword = os.Getenv("ARM_WINDOWS_ADMIN_PASSWORD") + + useManagedIdentityExtensionFromEnv := os.Getenv("ARM_USE_MANAGED_IDENTITY_EXTENSION") + if len(useManagedIdentityExtensionFromEnv) > 0 { + cfg.UseManagedIdentityExtension, err = strconv.ParseBool(useManagedIdentityExtensionFromEnv) + if err != nil { + return nil, err + } + } + } + + // Defaulting vmType to vmss. + if cfg.VMType == "" { + cfg.VMType = vmTypeVMSS + } + + env := azure.PublicCloud + if cfg.Cloud != "" { + env, err = azure.EnvironmentFromName(cfg.Cloud) + if err != nil { + return nil, err + } } - if resourceGroup == "" { - panic("Resource group not found") + if cfg.ResourceGroup == "" { + return nil, fmt.Errorf("resource group not set") } - if subscriptionId == "" { - panic("Subscription ID not found") + if cfg.SubscriptionID == "" { + return nil, fmt.Errorf("subscription ID not set") } - if tenantId == "" { - panic("Tenant ID not found.") + if cfg.TenantID == "" { + return nil, fmt.Errorf("tenant ID not set") } - if clientId == "" { - panic("ARM Client ID not found") + if cfg.AADClientID == "" { + return nil, fmt.Errorf("ARM Client ID not set") } - if clientSecret == "" { - panic("ARM Client Secret not found.") + if cfg.VMType == vmTypeStandard { + if cfg.Deployment == "" { + return nil, fmt.Errorf("deployment not set") + } + + if cfg.APIServerPrivateKey == "" { + return nil, fmt.Errorf("apiServerPrivateKey not set") + } + + if cfg.CAPrivateKey == "" { + return nil, fmt.Errorf("caPrivateKey not set") + } + + if cfg.ClientPrivateKey == "" { + return nil, fmt.Errorf("clientPrivateKey not set") + } + + if cfg.KubeConfigPrivateKey == "" { + return nil, fmt.Errorf("kubeConfigPrivateKey not set") + } } - glog.Infof("read configuration: %v", subscriptionId) + glog.Infof("Starting azure manager with subscription ID %q", cfg.SubscriptionID) - spt, err := NewServicePrincipalTokenFromCredentials(tenantId, clientId, clientSecret, azure.PublicCloud.ServiceManagementEndpoint) + spt, err := NewServicePrincipalTokenFromCredentials(&cfg, &env) if err != nil { - panic(err) + return nil, err } - scaleSetAPI = compute.NewVirtualMachineScaleSetsClient(subscriptionId) - scaleSetsClient := scaleSetAPI.(compute.VirtualMachineScaleSetsClient) + scaleSetsClient := compute.NewVirtualMachineScaleSetsClient(cfg.SubscriptionID) + scaleSetsClient.BaseURI = env.ResourceManagerEndpoint scaleSetsClient.Authorizer = autorest.NewBearerAuthorizer(spt) - scaleSetsClient.Sender = autorest.CreateSender() - - glog.Infof("Created scale set client with authorizer: %v", scaleSetsClient) + scaleSetsClient.PollingDelay = 5 * time.Second + configureUserAgent(&scaleSetsClient.Client) + glog.V(5).Infof("Created scale set client with authorizer: %v", scaleSetsClient) - scaleSetVmAPI = compute.NewVirtualMachineScaleSetVMsClient(subscriptionId) - scaleSetVMsClient := scaleSetVmAPI.(compute.VirtualMachineScaleSetVMsClient) + scaleSetVMsClient := compute.NewVirtualMachineScaleSetVMsClient(cfg.SubscriptionID) + scaleSetVMsClient.BaseURI = env.ResourceManagerEndpoint scaleSetVMsClient.Authorizer = autorest.NewBearerAuthorizer(spt) - scaleSetVMsClient.RequestInspector = withInspection() - scaleSetVMsClient.ResponseInspector = byInspecting() - - glog.Infof("Created scale set vm client with authorizer: %v", scaleSetVMsClient) - - // Create Availability Sets Azure Client. + scaleSetVMsClient.PollingDelay = 5 * time.Second + configureUserAgent(&scaleSetVMsClient.Client) + glog.V(5).Infof("Created scale set vm client with authorizer: %v", scaleSetVMsClient) + + virtualMachinesClient := compute.NewVirtualMachinesClient(cfg.SubscriptionID) + virtualMachinesClient.BaseURI = env.ResourceManagerEndpoint + virtualMachinesClient.Authorizer = autorest.NewBearerAuthorizer(spt) + virtualMachinesClient.PollingDelay = 5 * time.Second + configureUserAgent(&virtualMachinesClient.Client) + glog.V(5).Infof("Created vm client with authorizer: %v", virtualMachinesClient) + + deploymentsClient := resources.NewDeploymentsClient(cfg.SubscriptionID) + deploymentsClient.BaseURI = env.ResourceManagerEndpoint + deploymentsClient.Authorizer = autorest.NewBearerAuthorizer(spt) + deploymentsClient.PollingDelay = 5 * time.Second + configureUserAgent(&deploymentsClient.Client) + glog.V(5).Infof("Created deployments client with authorizer: %v", deploymentsClient) + + interfacesClient := network.NewInterfacesClient(cfg.SubscriptionID) + interfacesClient.BaseURI = env.ResourceManagerEndpoint + interfacesClient.Authorizer = autorest.NewBearerAuthorizer(spt) + interfacesClient.PollingDelay = 5 * time.Second + glog.V(5).Infof("Created interfaces client with authorizer: %v", interfacesClient) + + storageAccountsClient := storage.NewAccountsClient(cfg.SubscriptionID) + storageAccountsClient.BaseURI = env.ResourceManagerEndpoint + storageAccountsClient.Authorizer = autorest.NewBearerAuthorizer(spt) + storageAccountsClient.PollingDelay = 5 * time.Second + glog.V(5).Infof("Created storage accounts client with authorizer: %v", storageAccountsClient) + + disksClient := disk.NewDisksClient(cfg.SubscriptionID) + disksClient.BaseURI = env.ResourceManagerEndpoint + disksClient.Authorizer = autorest.NewBearerAuthorizer(spt) + disksClient.PollingDelay = 5 * time.Second + glog.V(5).Infof("Created disks client with authorizer: %v", disksClient) + + // Create azure manager. manager := &AzureManager{ - subscription: subscriptionId, - resourceGroupName: resourceGroup, - scaleSetClient: scaleSetsClient, - scaleSetVmClient: scaleSetVMsClient, - scaleSets: make([]*scaleSetInformation, 0), - scaleSetCache: make(map[AzureRef]*ScaleSet), - interrupt: make(chan struct{}), + config: &cfg, + env: env, + disksClient: disksClient, + interfacesClient: interfacesClient, + virtualMachineScaleSetsClient: scaleSetsClient, + virtualMachineScaleSetVMsClient: scaleSetVMsClient, + deploymentsClient: deploymentsClient, + virtualMachinesClient: virtualMachinesClient, + storageAccountsClient: storageAccountsClient, + + interrupt: make(chan struct{}), + instanceIDsCache: make(map[string]string), + nodeGroups: make([]cloudprovider.NodeGroup, 0), + nodeGroupsCache: make(map[AzureRef]cloudprovider.NodeGroup), } go wait.Until(func() { @@ -173,184 +299,237 @@ func CreateAzureManager(configReader io.Reader) (*AzureManager, error) { if err := manager.regenerateCache(); err != nil { glog.Errorf("Error while regenerating AS cache: %v", err) } - }, time.Hour, manager.interrupt) + }, 5*time.Minute, manager.interrupt) return manager, nil } // NewServicePrincipalTokenFromCredentials creates a new ServicePrincipalToken using values of the // passed credentials map. -func NewServicePrincipalTokenFromCredentials(tenantID string, clientID string, clientSecret string, scope string) (*adal.ServicePrincipalToken, error) { - oauthConfig, err := adal.NewOAuthConfig(azure.PublicCloud.ActiveDirectoryEndpoint, tenantID) +func NewServicePrincipalTokenFromCredentials(config *Config, env *azure.Environment) (*adal.ServicePrincipalToken, error) { + oauthConfig, err := adal.NewOAuthConfig(env.ActiveDirectoryEndpoint, config.TenantID) if err != nil { - panic(err) + return nil, fmt.Errorf("creating the OAuth config: %v", err) } - return adal.NewServicePrincipalToken(*oauthConfig, clientID, clientSecret, scope) -} -func withInspection() autorest.PrepareDecorator { - return func(p autorest.Preparer) autorest.Preparer { - return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) { - glog.Infof("Inspecting Request: %s %s\n", r.Method, r.URL) - return p.Prepare(r) - }) + if config.UseManagedIdentityExtension { + glog.V(2).Infoln("azure: using managed identity extension to retrieve access token") + msiEndpoint, err := adal.GetMSIVMEndpoint() + if err != nil { + return nil, fmt.Errorf("Getting the managed service identity endpoint: %v", err) + } + return adal.NewServicePrincipalTokenFromMSI( + msiEndpoint, + env.ServiceManagementEndpoint) + } + + if len(config.AADClientSecret) > 0 { + glog.V(2).Infoln("azure: using client_id+client_secret to retrieve access token") + return adal.NewServicePrincipalToken( + *oauthConfig, + config.AADClientID, + config.AADClientSecret, + env.ServiceManagementEndpoint) } -} -func byInspecting() autorest.RespondDecorator { - return func(r autorest.Responder) autorest.Responder { - return autorest.ResponderFunc(func(resp *http.Response) error { - glog.Infof("Inspecting Response: %s for %s %s\n", resp.Status, resp.Request.Method, resp.Request.URL) - return r.Respond(resp) - }) + if len(config.AADClientCertPath) > 0 && len(config.AADClientCertPassword) > 0 { + glog.V(2).Infoln("azure: using jwt client_assertion (client_cert+client_private_key) to retrieve access token") + certData, err := ioutil.ReadFile(config.AADClientCertPath) + if err != nil { + return nil, fmt.Errorf("reading the client certificate from file %s: %v", config.AADClientCertPath, err) + } + certificate, privateKey, err := decodePkcs12(certData, config.AADClientCertPassword) + if err != nil { + return nil, fmt.Errorf("decoding the client certificate: %v", err) + } + return adal.NewServicePrincipalTokenFromCertificate( + *oauthConfig, + config.AADClientID, + certificate, + privateKey, + env.ServiceManagementEndpoint) } + + return nil, fmt.Errorf("No credentials provided for AAD application %s", config.AADClientID) } -// RegisterScaleSet registers scale set in Azure Manager. -func (m *AzureManager) RegisterScaleSet(scaleSet *ScaleSet) { +// RegisterNodeGroup registers node group in Azure Manager. +func (m *AzureManager) RegisterNodeGroup(nodeGroup cloudprovider.NodeGroup) { m.cacheMutex.Lock() defer m.cacheMutex.Unlock() - m.scaleSets = append(m.scaleSets, - &scaleSetInformation{ - config: scaleSet, - basename: scaleSet.Name, - }) - -} - -// GetScaleSetSize gets Scale Set size. -func (m *AzureManager) GetScaleSetSize(asConfig *ScaleSet) (int64, error) { - glog.V(5).Infof("Get scale set size: %v\n", asConfig) - set, err := m.scaleSetClient.Get(m.resourceGroupName, asConfig.Name) - if err != nil { - return -1, err - } - glog.V(5).Infof("Returning scale set capacity: %d\n", *set.Sku.Capacity) - return *set.Sku.Capacity, nil + m.nodeGroups = append(m.nodeGroups, nodeGroup) } -// SetScaleSetSize sets ScaleSet size. -func (m *AzureManager) SetScaleSetSize(asConfig *ScaleSet, size int64) error { - op, err := m.scaleSetClient.Get(m.resourceGroupName, asConfig.Name) - if err != nil { - return err +func (m *AzureManager) nodeGroupRegisted(nodeGroup string) bool { + for _, ng := range m.nodeGroups { + if nodeGroup == ng.Id() { + return true + } } - op.Sku.Capacity = &size - op.VirtualMachineScaleSetProperties.ProvisioningState = nil - cancel := make(chan struct{}) - _, errChan := m.scaleSetClient.CreateOrUpdate(m.resourceGroupName, asConfig.Name, op, cancel) - return <-errChan + return false } -// GetScaleSetForInstance returns ScaleSetConfig of the given Instance -func (m *AzureManager) GetScaleSetForInstance(instance *AzureRef) (*ScaleSet, error) { - glog.V(5).Infof("Looking for scale set for instance: %v\n", instance) +// GetNodeGroupForInstance returns nodeGroup of the given Instance +func (m *AzureManager) GetNodeGroupForInstance(instance *AzureRef) (cloudprovider.NodeGroup, error) { + glog.V(5).Infof("Looking for node group for instance: %q", instance) - glog.V(8).Infof("Cache BEFORE: %v\n", m.scaleSetCache) + glog.V(8).Infof("Cache BEFORE: %v\n", m.nodeGroupsCache) m.cacheMutex.Lock() defer m.cacheMutex.Unlock() - if config, found := m.scaleSetCache[*instance]; found { - return config, nil + if nodeGroup, found := m.nodeGroupsCache[*instance]; found { + return nodeGroup, nil } if err := m.regenerateCache(); err != nil { - return nil, fmt.Errorf("Error while looking for ScaleSet for instance %+v, error: %v", *instance, err) + return nil, fmt.Errorf("Error while looking for nodeGroup for instance %+v, error: %v", *instance, err) } - glog.V(8).Infof("Cache AFTER: %v\n", m.scaleSetCache) + glog.V(8).Infof("Cache AFTER: %v\n", m.nodeGroupsCache) - if config, found := m.scaleSetCache[*instance]; found { - return config, nil + if nodeGroup, found := m.nodeGroupsCache[*instance]; found { + return nodeGroup, nil } - // instance does not belong to any configured Scale Set + + // instance does not belong to any configured nodeGroup. return nil, nil } -// DeleteInstances deletes the given instances. All instances must be controlled by the same ASG. -func (m *AzureManager) DeleteInstances(instances []*AzureRef) error { - if len(instances) == 0 { - return nil +// GetInstanceIDs gets instanceIDs for specified instances. +func (m *AzureManager) GetInstanceIDs(instances []*AzureRef) []string { + m.cacheMutex.Lock() + defer m.cacheMutex.Unlock() + + instanceIds := make([]string, len(instances)) + for i, instance := range instances { + instanceIds[i] = m.instanceIDsCache[instance.Name] + } + + return instanceIds +} + +func (m *AzureManager) regenerateCache() (err error) { + var newCache map[AzureRef]cloudprovider.NodeGroup + var newInstanceIDsCache map[string]string + + switch m.config.VMType { + case vmTypeVMSS: + newCache, newInstanceIDsCache, err = m.listScaleSets() + case vmTypeStandard: + newCache, newInstanceIDsCache, err = m.listAgentPools() + default: + err = fmt.Errorf("vmType %q not supported", m.config.VMType) } - commonAsg, err := m.GetScaleSetForInstance(instances[0]) if err != nil { return err } - for _, instance := range instances { - asg, err := m.GetScaleSetForInstance(instance) - if err != nil { - return err - } - if asg != commonAsg { - return fmt.Errorf("cannot delete instance (%s) which don't belong to the same Scale Set", instance.GetKey()) + + m.nodeGroupsCache = newCache + m.instanceIDsCache = newInstanceIDsCache + return nil +} + +func (m *AzureManager) getNodeGroupByID(id string) cloudprovider.NodeGroup { + for _, ng := range m.nodeGroups { + if id == ng.Id() { + return ng } } - instanceIds := make([]string, len(instances)) - for i, instance := range instances { - instanceIds[i] = m.scaleSetIdCache[instance.Name] - } - requiredIds := &compute.VirtualMachineScaleSetVMInstanceRequiredIDs{ - InstanceIds: &instanceIds, - } - cancel := make(chan struct{}) - _, errChan := m.scaleSetClient.DeleteInstances(m.resourceGroupName, commonAsg.Name, *requiredIds, cancel) - return <-errChan + return nil } -func (m *AzureManager) regenerateCache() error { - newCache := make(map[AzureRef]*ScaleSet) - newScaleSetIdCache := make(map[string]string) +func (m *AzureManager) listAgentPools() (map[AzureRef]cloudprovider.NodeGroup, map[string]string, error) { + as := make(map[AzureRef]cloudprovider.NodeGroup) + instanceIDs := make(map[string]string) - for _, sset := range m.scaleSets { - glog.V(4).Infof("Regenerating Scale Set information for %s", sset.config.Name) - scaleSet, err := m.scaleSetClient.Get(m.resourceGroupName, sset.config.Name) - if err != nil { - glog.Errorf("Failed to get scaleSet with name %s: %v", sset.config.Name, err) - return err + for _, nodeGroup := range m.nodeGroups { + agentPool, ok := nodeGroup.(*AgentPool) + if !ok { + return nil, nil, fmt.Errorf("node group %q is not AgentPool", nodeGroup) } - sset.basename = *scaleSet.Name - result, err := m.scaleSetVmClient.List(m.resourceGroupName, sset.basename, "", "", "") + _, vmIndex, err := agentPool.GetVMIndexes() if err != nil { - glog.Errorf("Failed to list vm for scaleSet %s: %v", sset.config.Name, err) - return err + glog.Errorf("GetVMIndexes for node group %q failed: %v", nodeGroup.Id(), err) + return nil, nil, err } - for _, instance := range *result.Value { - // Convert to lower because instance.ID is in different in different API calls (e.g. GET and LIST). - name := "azure://" + strings.ToLower(*instance.ID) - ref := AzureRef{ - Name: name, + for idx := range vmIndex { + id := vmIndex[idx].ID + vmID := vmIndex[idx].VMID + + idRef := AzureRef{ + Name: id, + } + vmIDRef := AzureRef{ + Name: vmID, } - newCache[ref] = sset.config - newScaleSetIdCache[name] = *instance.InstanceID + as[idRef] = nodeGroup + as[vmIDRef] = nodeGroup + instanceIDs[id] = fmt.Sprintf("%d", idx) + instanceIDs[vmID] = fmt.Sprintf("%d", idx) } } - m.scaleSetCache = newCache - m.scaleSetIdCache = newScaleSetIdCache - return nil + return as, instanceIDs, nil } -// GetScaleSetVms returns list of nodes for the given scale set. -func (m *AzureManager) GetScaleSetVms(scaleSet *ScaleSet) ([]string, error) { - instances, err := m.scaleSetVmClient.List(m.resourceGroupName, scaleSet.Name, "", "", "") +// listScaleSets gets a list of scale sets and instanceIDs. +func (m *AzureManager) listScaleSets() (map[AzureRef]cloudprovider.NodeGroup, map[string]string, error) { + var err error + scaleSets := make(map[AzureRef]cloudprovider.NodeGroup) + instanceIDs := make(map[string]string) - if err != nil { - glog.V(4).Infof("Failed AS info request for %s: %v", scaleSet.Name, err) - return []string{}, err - } - result := make([]string, 0) - for _, instance := range *instances.Value { - // Convert to lower because instance.ID is in different in different API calls (e.g. GET and LIST). - name := "azure://" + strings.ToLower(*instance.ID) - result = append(result, name) + for _, sset := range m.nodeGroups { + glog.V(4).Infof("Listing Scale Set information for %s", sset.Id()) + + resourceGroup := m.config.ResourceGroup + ssInfo, err := m.virtualMachineScaleSetsClient.Get(resourceGroup, sset.Id()) + if err != nil { + glog.Errorf("Failed to get scaleSet with name %s: %v", sset.Id(), err) + return nil, nil, err + } + + result, err := m.virtualMachineScaleSetVMsClient.List(resourceGroup, *ssInfo.Name, "", "", "") + if err != nil { + glog.Errorf("Failed to list vm for scaleSet %s: %v", *ssInfo.Name, err) + return nil, nil, err + } + + moreResult := (result.Value != nil && len(*result.Value) > 0) + for moreResult { + for _, instance := range *result.Value { + // Convert to lower because instance.ID is in different in different API calls (e.g. GET and LIST). + name := "azure://" + strings.ToLower(*instance.ID) + vmID := "azure://" + strings.ToLower(*instance.VMID) + ref := AzureRef{ + Name: name, + } + vmIDRef := AzureRef{ + Name: vmID, + } + scaleSets[ref] = sset + scaleSets[vmIDRef] = sset + instanceIDs[name] = *instance.InstanceID + } + + moreResult = false + if result.NextLink != nil { + result, err = m.virtualMachineScaleSetVMsClient.ListNextResults(result) + if err != nil { + glog.Errorf("virtualMachineScaleSetVMsClient.ListNextResults failed: %v", err) + return nil, nil, err + } + + moreResult = (result.Value != nil && len(*result.Value) > 0) + } + } } - return result, nil + return scaleSets, instanceIDs, err } // Cleanup closes the channel to signal the go routine to stop that is handling the cache diff --git a/cluster-autoscaler/cloudprovider/azure/azure_scale_set.go b/cluster-autoscaler/cloudprovider/azure/azure_scale_set.go new file mode 100644 index 000000000000..608a2b59bd18 --- /dev/null +++ b/cluster-autoscaler/cloudprovider/azure/azure_scale_set.go @@ -0,0 +1,290 @@ +/* +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 azure + +import ( + "fmt" + "strings" + + "github.com/Azure/azure-sdk-for-go/arm/compute" + "github.com/golang/glog" + + apiv1 "k8s.io/api/core/v1" + "k8s.io/autoscaler/cluster-autoscaler/cloudprovider" + "k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache" +) + +// ScaleSet implements NodeGroup interface. +type ScaleSet struct { + AzureRef + *AzureManager + + minSize int + maxSize int +} + +// NewScaleSet creates a new NewScaleSet. +func NewScaleSet(name string, minSize, maxSize int, az *AzureManager) (*ScaleSet, error) { + scaleSet := &ScaleSet{ + AzureRef: AzureRef{ + Name: name, + }, + minSize: minSize, + maxSize: maxSize, + AzureManager: az, + } + + return scaleSet, nil +} + +// MinSize returns minimum size of the node group. +func (scaleSet *ScaleSet) MinSize() int { + return scaleSet.minSize +} + +// Exist checks if the node group really exists on the cloud provider side. Allows to tell the +// theoretical node group from the real one. +func (scaleSet *ScaleSet) Exist() bool { + return true +} + +// Create creates the node group on the cloud provider side. +func (scaleSet *ScaleSet) Create() error { + return cloudprovider.ErrAlreadyExist +} + +// Delete deletes the node group on the cloud provider side. +// This will be executed only for autoprovisioned node groups, once their size drops to 0. +func (scaleSet *ScaleSet) Delete() error { + return cloudprovider.ErrNotImplemented +} + +// Autoprovisioned returns true if the node group is autoprovisioned. +func (scaleSet *ScaleSet) Autoprovisioned() bool { + return false +} + +// MaxSize returns maximum size of the node group. +func (scaleSet *ScaleSet) MaxSize() int { + return scaleSet.maxSize +} + +// GetScaleSetSize gets Scale Set size. +func (scaleSet *ScaleSet) GetScaleSetSize() (int64, error) { + glog.V(5).Infof("Get scale set size for %q", scaleSet.Name) + resourceGroup := scaleSet.config.ResourceGroup + set, err := scaleSet.virtualMachineScaleSetsClient.Get(resourceGroup, scaleSet.Name) + if err != nil { + return -1, err + } + glog.V(5).Infof("Returning scale set (%q) capacity: %d\n", scaleSet.Name, *set.Sku.Capacity) + return *set.Sku.Capacity, nil +} + +// SetScaleSetSize sets ScaleSet size. +func (scaleSet *ScaleSet) SetScaleSetSize(size int64) error { + resourceGroup := scaleSet.config.ResourceGroup + op, err := scaleSet.virtualMachineScaleSetsClient.Get(resourceGroup, scaleSet.Name) + if err != nil { + return err + } + + op.Sku.Capacity = &size + op.VirtualMachineScaleSetProperties.ProvisioningState = nil + cancel := make(chan struct{}) + + _, errChan := scaleSet.virtualMachineScaleSetsClient.CreateOrUpdate(resourceGroup, scaleSet.Name, op, cancel) + return <-errChan +} + +// TargetSize returns the current TARGET size of the node group. It is possible that the +// number is different from the number of nodes registered in Kubernetes. +func (scaleSet *ScaleSet) TargetSize() (int, error) { + size, err := scaleSet.GetScaleSetSize() + return int(size), err +} + +// IncreaseSize increases Scale Set size +func (scaleSet *ScaleSet) IncreaseSize(delta int) error { + if delta <= 0 { + return fmt.Errorf("size increase must be positive") + } + + size, err := scaleSet.GetScaleSetSize() + if err != nil { + return err + } + + if int(size)+delta > scaleSet.MaxSize() { + return fmt.Errorf("size increase too large - desired:%d max:%d", int(size)+delta, scaleSet.MaxSize()) + } + + return scaleSet.SetScaleSetSize(size + int64(delta)) +} + +// GetScaleSetVms returns list of nodes for the given scale set. +func (scaleSet *ScaleSet) GetScaleSetVms() ([]string, error) { + resourceGroup := scaleSet.config.ResourceGroup + instances, err := scaleSet.virtualMachineScaleSetVMsClient.List(resourceGroup, scaleSet.Name, "", "", "") + + if err != nil { + glog.V(4).Infof("VirtualMachineScaleSetVMsClient.List failed for %s: %v", scaleSet.Name, err) + return []string{}, err + } + + result := make([]string, 0) + for _, instance := range *instances.Value { + // Convert to lower because instance.ID is in different in different API calls (e.g. GET and LIST). + name := "azure://" + strings.ToLower(*instance.ID) + result = append(result, name) + } + return result, nil + +} + +// DecreaseTargetSize decreases the target size of the node group. This function +// doesn't permit to delete any existing node and can be used only to reduce the +// request for new nodes that have not been yet fulfilled. Delta should be negative. +// It is assumed that cloud provider will not delete the existing nodes if the size +// when there is an option to just decrease the target. +func (scaleSet *ScaleSet) DecreaseTargetSize(delta int) error { + if delta >= 0 { + return fmt.Errorf("size decrease size must be negative") + } + + size, err := scaleSet.GetScaleSetSize() + if err != nil { + return err + } + + nodes, err := scaleSet.GetScaleSetVms() + if err != nil { + return err + } + + if int(size)+delta < len(nodes) { + return fmt.Errorf("attempt to delete existing nodes targetSize:%d delta:%d existingNodes: %d", + size, delta, len(nodes)) + } + + return scaleSet.SetScaleSetSize(size + int64(delta)) +} + +// Belongs returns true if the given node belongs to the NodeGroup. +func (scaleSet *ScaleSet) Belongs(node *apiv1.Node) (bool, error) { + glog.V(6).Infof("Check if node belongs to this scale set: scaleset:%v, node:%v\n", scaleSet, node) + + ref := &AzureRef{ + Name: strings.ToLower(node.Spec.ProviderID), + } + + targetAsg, err := scaleSet.GetNodeGroupForInstance(ref) + if err != nil { + return false, err + } + if targetAsg == nil { + return false, fmt.Errorf("%s doesn't belong to a known scale set", node.Name) + } + if targetAsg.Id() != scaleSet.Id() { + return false, nil + } + return true, nil +} + +// DeleteInstances deletes the given instances. All instances must be controlled by the same ASG. +func (scaleSet *ScaleSet) DeleteInstances(instances []*AzureRef) error { + if len(instances) == 0 { + return nil + } + + commonAsg, err := scaleSet.GetNodeGroupForInstance(instances[0]) + if err != nil { + return err + } + + for _, instance := range instances { + asg, err := scaleSet.GetNodeGroupForInstance(instance) + if err != nil { + return err + } + + if asg != commonAsg { + return fmt.Errorf("cannot delete instance (%s) which don't belong to the same Scale Set (%q)", instance.GetKey(), commonAsg) + } + } + + instanceIds := scaleSet.GetInstanceIDs(instances) + requiredIds := &compute.VirtualMachineScaleSetVMInstanceRequiredIDs{ + InstanceIds: &instanceIds, + } + cancel := make(chan struct{}) + resourceGroup := scaleSet.config.ResourceGroup + _, errChan := scaleSet.virtualMachineScaleSetsClient.DeleteInstances(resourceGroup, commonAsg.Id(), *requiredIds, cancel) + return <-errChan +} + +// DeleteNodes deletes the nodes from the group. +func (scaleSet *ScaleSet) DeleteNodes(nodes []*apiv1.Node) error { + glog.V(8).Infof("Delete nodes requested: %v\n", nodes) + size, err := scaleSet.GetScaleSetSize() + if err != nil { + return err + } + + if int(size) <= scaleSet.MinSize() { + return fmt.Errorf("min size reached, nodes will not be deleted") + } + + refs := make([]*AzureRef, 0, len(nodes)) + for _, node := range nodes { + belongs, err := scaleSet.Belongs(node) + if err != nil { + return err + } + + if belongs != true { + return fmt.Errorf("%s belongs to a different asg than %s", node.Name, scaleSet.Id()) + } + + azureRef := &AzureRef{ + Name: strings.ToLower(node.Spec.ProviderID), + } + refs = append(refs, azureRef) + } + + return scaleSet.DeleteInstances(refs) +} + +// Id returns ScaleSet id. +func (scaleSet *ScaleSet) Id() string { + return scaleSet.Name +} + +// Debug returns a debug string for the Scale Set. +func (scaleSet *ScaleSet) Debug() string { + return fmt.Sprintf("%s (%d:%d)", scaleSet.Id(), scaleSet.MinSize(), scaleSet.MaxSize()) +} + +// TemplateNodeInfo returns a node template for this scale set. +func (scaleSet *ScaleSet) TemplateNodeInfo() (*schedulercache.NodeInfo, error) { + return nil, cloudprovider.ErrNotImplemented +} + +// Nodes returns a list of all nodes that belong to this node group. +func (scaleSet *ScaleSet) Nodes() ([]string, error) { + return scaleSet.GetScaleSetVms() +} diff --git a/cluster-autoscaler/cloudprovider/azure/azure_scale_set_test.go b/cluster-autoscaler/cloudprovider/azure/azure_scale_set_test.go new file mode 100644 index 000000000000..c7c21034d6e5 --- /dev/null +++ b/cluster-autoscaler/cloudprovider/azure/azure_scale_set_test.go @@ -0,0 +1,153 @@ +/* +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 azure + +import ( + "net/http" + "testing" + + "github.com/Azure/go-autorest/autorest" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + + apiv1 "k8s.io/api/core/v1" + "k8s.io/autoscaler/cluster-autoscaler/cloudprovider" +) + +func TestMaxSize(t *testing.T) { + provider, err := newTestProvider() + assert.NoError(t, err) + + err = provider.addNodeGroup("1:5:test-asg") + assert.NoError(t, err) + assert.Equal(t, len(provider.nodeGroups), 1) + assert.Equal(t, provider.nodeGroups[0].MaxSize(), 5) +} + +func TestMinSize(t *testing.T) { + provider, err := newTestProvider() + assert.NoError(t, err) + + err = provider.addNodeGroup("1:5:test-asg") + assert.NoError(t, err) + assert.Equal(t, len(provider.nodeGroups), 1) + assert.Equal(t, provider.nodeGroups[0].MinSize(), 1) +} + +func TestTargetSize(t *testing.T) { + provider, err := newTestProvider() + assert.NoError(t, err) + + err = provider.addNodeGroup("1:5:test-asg") + assert.NoError(t, err) + targetSize, err := provider.nodeGroups[0].TargetSize() + assert.Equal(t, targetSize, 2) + assert.NoError(t, err) +} + +func TestIncreaseSize(t *testing.T) { + provider, err := newTestProvider() + assert.NoError(t, err) + + err = provider.addNodeGroup("1:5:test-asg") + assert.NoError(t, err) + assert.Equal(t, len(provider.nodeGroups), 1) + + err = provider.nodeGroups[0].IncreaseSize(1) + assert.NoError(t, err) +} + +func TestBelongs(t *testing.T) { + provider, err := newTestProvider() + assert.NoError(t, err) + + err = provider.addNodeGroup("1:5:test-asg") + assert.NoError(t, err) + + scaleSet, ok := provider.nodeGroups[0].(*ScaleSet) + assert.True(t, ok) + + invalidNode := &apiv1.Node{ + Spec: apiv1.NodeSpec{ + ProviderID: "azure:///subscriptions/subscriptionId/resourceGroups/kubernetes/providers/Microsoft.Compute/virtualMachines/invalid-instance-id", + }, + } + _, err = scaleSet.Belongs(invalidNode) + assert.Error(t, err) + + validNode := &apiv1.Node{ + Spec: apiv1.NodeSpec{ + ProviderID: "azure://123E4567-E89B-12D3-A456-426655440000", + }, + } + + belongs, err := scaleSet.Belongs(validNode) + assert.Equal(t, true, belongs) + assert.NoError(t, err) +} + +func TestDeleteNodes(t *testing.T) { + manager := newTestAzureManager() + scaleSetClient := &VirtualMachineScaleSetsClientMock{} + instanceIds := make([]string, 1) + instanceIds[0] = "test-instance-id" + response := autorest.Response{ + Response: &http.Response{ + Status: "OK", + }, + } + scaleSetClient.On("DeleteInstances", mock.Anything, "test-asg", mock.Anything, mock.Anything).Return(response, nil) + manager.virtualMachineScaleSetsClient = scaleSetClient + + resourceLimiter := cloudprovider.NewResourceLimiter( + map[string]int64{cloudprovider.ResourceNameCores: 1, cloudprovider.ResourceNameMemory: 10000000}, + map[string]int64{cloudprovider.ResourceNameCores: 10, cloudprovider.ResourceNameMemory: 100000000}) + provider, err := BuildAzureCloudProvider(manager, nil, resourceLimiter) + assert.NoError(t, err) + err = provider.addNodeGroup("1:5:test-asg") + assert.NoError(t, err) + + node := &apiv1.Node{ + Spec: apiv1.NodeSpec{ + ProviderID: "azure://123E4567-E89B-12D3-A456-426655440000", + }, + } + scaleSet, ok := provider.nodeGroups[0].(*ScaleSet) + assert.True(t, ok) + err = scaleSet.DeleteNodes([]*apiv1.Node{node}) + assert.NoError(t, err) + scaleSetClient.AssertNumberOfCalls(t, "DeleteInstances", 1) +} + +func TestId(t *testing.T) { + provider, err := newTestProvider() + assert.NoError(t, err) + err = provider.addNodeGroup("1:5:test-asg") + assert.NoError(t, err) + assert.Equal(t, len(provider.nodeGroups), 1) + assert.Equal(t, provider.nodeGroups[0].Id(), "test-asg") +} + +func TestDebug(t *testing.T) { + asg := ScaleSet{ + AzureManager: newTestAzureManager(), + minSize: 5, + maxSize: 55, + } + asg.Name = "test-scale-set" + assert.Equal(t, asg.Debug(), "test-scale-set (5:55)") +} diff --git a/cluster-autoscaler/cloudprovider/azure/azure_util.go b/cluster-autoscaler/cloudprovider/azure/azure_util.go new file mode 100644 index 000000000000..623082798344 --- /dev/null +++ b/cluster-autoscaler/cloudprovider/azure/azure_util.go @@ -0,0 +1,349 @@ +/* +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 azure + +import ( + "crypto/rsa" + "crypto/x509" + "fmt" + "net/url" + "regexp" + "sort" + "strconv" + "strings" + + "github.com/Azure/azure-sdk-for-go/arm/compute" + "github.com/Azure/go-autorest/autorest" + "github.com/golang/glog" + "golang.org/x/crypto/pkcs12" + "k8s.io/client-go/pkg/version" +) + +const ( + //Field names + customDataFieldName = "customData" + dependsOnFieldName = "dependsOn" + hardwareProfileFieldName = "hardwareProfile" + imageReferenceFieldName = "imageReference" + nameFieldName = "name" + osProfileFieldName = "osProfile" + propertiesFieldName = "properties" + resourcesFieldName = "resources" + storageProfileFieldName = "storageProfile" + typeFieldName = "type" + vmSizeFieldName = "vmSize" + + // ARM resource Types + nsgResourceType = "Microsoft.Network/networkSecurityGroups" + rtResourceType = "Microsoft.Network/routeTables" + vmResourceType = "Microsoft.Compute/virtualMachines" + vmExtensionType = "Microsoft.Compute/virtualMachines/extensions" + + // resource ids + nsgID = "nsgID" + rtID = "routeTableID" + + k8sLinuxVMNamingFormat = "^[0-9a-zA-Z]{3}-(.+)-([0-9a-fA-F]{8})-{0,2}([0-9]+)$" + k8sLinuxVMAgentPoolNameIndex = 1 + k8sLinuxVMAgentClusterIDIndex = 2 + k8sLinuxVMAgentIndexArrayIndex = 3 + + k8sWindowsVMNamingFormat = "^([a-fA-F0-9]{5})([0-9a-zA-Z]{3})([a-zA-Z0-9]{4,6})$" + k8sWindowsVMAgentPoolPrefixIndex = 1 + k8sWindowsVMAgentOrchestratorNameIndex = 2 + k8sWindowsVMAgentPoolInfoIndex = 3 +) + +var ( + vmnameLinuxRegexp = regexp.MustCompile(k8sLinuxVMNamingFormat) + vmnameWindowsRegexp = regexp.MustCompile(k8sWindowsVMNamingFormat) +) + +// decodePkcs12 decodes a PKCS#12 client certificate by extracting the public certificate and +// the private RSA key +func decodePkcs12(pkcs []byte, password string) (*x509.Certificate, *rsa.PrivateKey, error) { + privateKey, certificate, err := pkcs12.Decode(pkcs, password) + if err != nil { + return nil, nil, fmt.Errorf("decoding the PKCS#12 client certificate: %v", err) + } + rsaPrivateKey, isRsaKey := privateKey.(*rsa.PrivateKey) + if !isRsaKey { + return nil, nil, fmt.Errorf("PKCS#12 certificate must contain a RSA private key") + } + + return certificate, rsaPrivateKey, nil +} + +// configureUserAgent configures the autorest client with a user agent that +// includes "autoscaler" and the full client version string +// example: +// Azure-SDK-for-Go/7.0.1-beta arm-network/2016-09-01; cluster-autoscaler/v1.7.0-alpha.2.711+a2fadef8170bb0-dirty; +func configureUserAgent(client *autorest.Client) { + k8sVersion := version.Get().GitVersion + client.UserAgent = fmt.Sprintf("%s; cluster-autoscaler/%s", client.UserAgent, k8sVersion) +} + +// normalizeForK8sVMASScalingUp takes a template and removes elements that are unwanted in a K8s VMAS scale up/down case +func normalizeForK8sVMASScalingUp(templateMap map[string]interface{}) error { + if err := normalizeMasterResourcesForScaling(templateMap); err != nil { + return err + } + rtIndex := -1 + nsgIndex := -1 + resources := templateMap[resourcesFieldName].([]interface{}) + for index, resource := range resources { + resourceMap, ok := resource.(map[string]interface{}) + if !ok { + glog.Warningf("Template improperly formatted for resource") + continue + } + + resourceType, ok := resourceMap[typeFieldName].(string) + if ok && resourceType == nsgResourceType { + if nsgIndex != -1 { + err := fmt.Errorf("Found 2 resources with type %s in the template. There should only be 1", nsgResourceType) + glog.Errorf(err.Error()) + return err + } + nsgIndex = index + } + if ok && resourceType == rtResourceType { + if rtIndex != -1 { + err := fmt.Errorf("Found 2 resources with type %s in the template. There should only be 1", rtResourceType) + glog.Warningf(err.Error()) + return err + } + rtIndex = index + } + + dependencies, ok := resourceMap[dependsOnFieldName].([]interface{}) + if !ok { + continue + } + + for dIndex := len(dependencies) - 1; dIndex >= 0; dIndex-- { + dependency := dependencies[dIndex].(string) + if strings.Contains(dependency, nsgResourceType) || strings.Contains(dependency, nsgID) || + strings.Contains(dependency, rtResourceType) || strings.Contains(dependency, rtID) { + dependencies = append(dependencies[:dIndex], dependencies[dIndex+1:]...) + } + } + + if len(dependencies) > 0 { + resourceMap[dependsOnFieldName] = dependencies + } else { + delete(resourceMap, dependsOnFieldName) + } + } + + indexesToRemove := []int{} + if nsgIndex == -1 { + err := fmt.Errorf("Found no resources with type %s in the template. There should have been 1", nsgResourceType) + glog.Errorf(err.Error()) + return err + } + if rtIndex == -1 { + glog.Infof("Found no resources with type %s in the template.", rtResourceType) + } else { + indexesToRemove = append(indexesToRemove, rtIndex) + } + indexesToRemove = append(indexesToRemove, nsgIndex) + templateMap[resourcesFieldName] = removeIndexesFromArray(resources, indexesToRemove) + + return nil +} + +func removeIndexesFromArray(array []interface{}, indexes []int) []interface{} { + sort.Sort(sort.Reverse(sort.IntSlice(indexes))) + for _, index := range indexes { + array = append(array[:index], array[index+1:]...) + } + return array +} + +// normalizeMasterResourcesForScaling takes a template and removes elements that are unwanted in any scale up/down case +func normalizeMasterResourcesForScaling(templateMap map[string]interface{}) error { + resources := templateMap[resourcesFieldName].([]interface{}) + indexesToRemove := []int{} + //update master nodes resources + for index, resource := range resources { + resourceMap, ok := resource.(map[string]interface{}) + if !ok { + glog.Warningf("Template improperly formatted") + continue + } + + resourceType, ok := resourceMap[typeFieldName].(string) + if !ok || resourceType != vmResourceType { + resourceName, ok := resourceMap[nameFieldName].(string) + if !ok { + glog.Warningf("Template improperly formatted") + continue + } + if strings.Contains(resourceName, "variables('masterVMNamePrefix')") && resourceType == vmExtensionType { + indexesToRemove = append(indexesToRemove, index) + } + continue + } + + resourceName, ok := resourceMap[nameFieldName].(string) + if !ok { + glog.Warningf("Template improperly formatted") + continue + } + + // make sure this is only modifying the master vms + if !strings.Contains(resourceName, "variables('masterVMNamePrefix')") { + continue + } + + resourceProperties, ok := resourceMap[propertiesFieldName].(map[string]interface{}) + if !ok { + glog.Warningf("Template improperly formatted") + continue + } + + hardwareProfile, ok := resourceProperties[hardwareProfileFieldName].(map[string]interface{}) + if !ok { + glog.Warningf("Template improperly formatted") + continue + } + + if hardwareProfile[vmSizeFieldName] != nil { + delete(hardwareProfile, vmSizeFieldName) + } + + if !removeCustomData(resourceProperties) || !removeImageReference(resourceProperties) { + continue + } + } + templateMap[resourcesFieldName] = removeIndexesFromArray(resources, indexesToRemove) + + return nil +} + +func removeCustomData(resourceProperties map[string]interface{}) bool { + osProfile, ok := resourceProperties[osProfileFieldName].(map[string]interface{}) + if !ok { + glog.Warningf("Template improperly formatted") + return ok + } + + if osProfile[customDataFieldName] != nil { + delete(osProfile, customDataFieldName) + } + return ok +} + +func removeImageReference(resourceProperties map[string]interface{}) bool { + storageProfile, ok := resourceProperties[storageProfileFieldName].(map[string]interface{}) + if !ok { + glog.Warningf("Template improperly formatted. Could not find: %s", storageProfileFieldName) + return ok + } + + if storageProfile[imageReferenceFieldName] != nil { + delete(storageProfile, imageReferenceFieldName) + } + return ok +} + +// resourceName returns the last segment (the resource name) for the specified resource identifier. +func resourceName(ID string) (string, error) { + parts := strings.Split(ID, "/") + name := parts[len(parts)-1] + if len(name) == 0 { + return "", fmt.Errorf("resource name was missing from identifier") + } + + return name, nil +} + +// splitBlobURI returns a decomposed blob URI parts: accountName, containerName, blobName. +func splitBlobURI(URI string) (string, string, string, error) { + uri, err := url.Parse(URI) + if err != nil { + return "", "", "", err + } + + accountName := strings.Split(uri.Host, ".")[0] + urlParts := strings.Split(uri.Path, "/") + + containerName := urlParts[1] + blobPath := strings.Join(urlParts[2:], "/") + + return accountName, containerName, blobPath, nil +} + +// k8sLinuxVMNameParts returns parts of Linux VM name e.g: k8s-agentpool1-11290731-0 +func k8sLinuxVMNameParts(vmName string) (poolIdentifier, nameSuffix string, agentIndex int, err error) { + vmNameParts := vmnameLinuxRegexp.FindStringSubmatch(vmName) + if len(vmNameParts) != 4 { + return "", "", -1, fmt.Errorf("resource name was missing from identifier") + } + + vmNum, err := strconv.Atoi(vmNameParts[k8sLinuxVMAgentIndexArrayIndex]) + + if err != nil { + return "", "", -1, fmt.Errorf("Error parsing VM Name: %v", err) + } + + return vmNameParts[k8sLinuxVMAgentPoolNameIndex], vmNameParts[k8sLinuxVMAgentClusterIDIndex], vmNum, nil +} + +// windowsVMNameParts returns parts of Windows VM name e.g: 50621k8s9000 +func windowsVMNameParts(vmName string) (poolPrefix string, acsStr string, poolIndex int, agentIndex int, err error) { + vmNameParts := vmnameWindowsRegexp.FindStringSubmatch(vmName) + if len(vmNameParts) != 4 { + return "", "", -1, -1, fmt.Errorf("resource name was missing from identifier") + } + + poolPrefix = vmNameParts[k8sWindowsVMAgentPoolPrefixIndex] + acsStr = vmNameParts[k8sWindowsVMAgentOrchestratorNameIndex] + poolInfo := vmNameParts[k8sWindowsVMAgentPoolInfoIndex] + + poolIndex, err = strconv.Atoi(poolInfo[:3]) + if err != nil { + return "", "", -1, -1, fmt.Errorf("Error parsing VM Name: %v", err) + } + + agentIndex, err = strconv.Atoi(poolInfo[3:]) + if err != nil { + return "", "", -1, -1, fmt.Errorf("Error parsing VM Name: %v", err) + } + + return poolPrefix, acsStr, poolIndex, agentIndex, nil +} + +// GetVMNameIndex return the index of VM in the node pools. +func GetVMNameIndex(osType compute.OperatingSystemTypes, vmName string) (int, error) { + var agentIndex int + var err error + if osType == compute.Linux { + _, _, agentIndex, err = k8sLinuxVMNameParts(vmName) + if err != nil { + return 0, err + } + } else if osType == compute.Windows { + _, _, _, agentIndex, err = windowsVMNameParts(vmName) + if err != nil { + return 0, err + } + } + + return agentIndex, nil +} diff --git a/cluster-autoscaler/cloudprovider/azure/azure_util_test.go b/cluster-autoscaler/cloudprovider/azure/azure_util_test.go new file mode 100644 index 000000000000..5b2a179f8460 --- /dev/null +++ b/cluster-autoscaler/cloudprovider/azure/azure_util_test.go @@ -0,0 +1,119 @@ +/* +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 azure + +import ( + "fmt" + "testing" + + "github.com/Azure/azure-sdk-for-go/arm/compute" +) + +func TestSplitBlobURI(t *testing.T) { + expectedAccountName := "vhdstorage8h8pjybi9hbsl6" + expectedContainerName := "vhds" + expectedBlobPath := "osdisks/disk1234.vhd" + accountName, containerName, blobPath, err := splitBlobURI("https://vhdstorage8h8pjybi9hbsl6.blob.core.windows.net/vhds/osdisks/disk1234.vhd") + if accountName != expectedAccountName { + t.Fatalf("incorrect account name. expected=%s actual=%s", expectedAccountName, accountName) + } + if containerName != expectedContainerName { + t.Fatalf("incorrect account name. expected=%s actual=%s", expectedContainerName, containerName) + } + if blobPath != expectedBlobPath { + t.Fatalf("incorrect account name. expected=%s actual=%s", expectedBlobPath, blobPath) + } + if err != nil { + t.Fatalf("unexpected error: %s", err) + } +} + +func TestK8sLinuxVMNameParts(t *testing.T) { + data := []struct { + poolIdentifier, nameSuffix string + agentIndex int + }{ + {"agentpool1", "38988164", 10}, + {"agent-pool1", "38988164", 8}, + {"agent-pool-1", "38988164", 0}, + } + + for _, el := range data { + vmName := fmt.Sprintf("k8s-%s-%s-%d", el.poolIdentifier, el.nameSuffix, el.agentIndex) + poolIdentifier, nameSuffix, agentIndex, err := k8sLinuxVMNameParts(vmName) + if poolIdentifier != el.poolIdentifier { + t.Fatalf("incorrect poolIdentifier. expected=%s actual=%s", el.poolIdentifier, poolIdentifier) + } + if nameSuffix != el.nameSuffix { + t.Fatalf("incorrect nameSuffix. expected=%s actual=%s", el.nameSuffix, nameSuffix) + } + if agentIndex != el.agentIndex { + t.Fatalf("incorrect agentIndex. expected=%d actual=%d", el.agentIndex, agentIndex) + } + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + } +} + +func TestWindowsVMNameParts(t *testing.T) { + expectedPoolPrefix := "38988" + expectedAcs := "k8s" + expectedPoolIndex := 903 + expectedAgentIndex := 12 + + poolPrefix, acs, poolIndex, agentIndex, err := windowsVMNameParts("38988k8s90312") + if poolPrefix != expectedPoolPrefix { + t.Fatalf("incorrect poolPrefix. expected=%s actual=%s", expectedPoolPrefix, poolPrefix) + } + if acs != expectedAcs { + t.Fatalf("incorrect acs string. expected=%s actual=%s", expectedAcs, acs) + } + if poolIndex != expectedPoolIndex { + t.Fatalf("incorrect poolIndex. expected=%d actual=%d", expectedPoolIndex, poolIndex) + } + if agentIndex != expectedAgentIndex { + t.Fatalf("incorrect agentIndex. expected=%d actual=%d", expectedAgentIndex, agentIndex) + } + if err != nil { + t.Fatalf("unexpected error: %s", err) + } +} + +func TestGetVMNameIndexLinux(t *testing.T) { + expectedAgentIndex := 65 + + agentIndex, err := GetVMNameIndex(compute.Linux, "k8s-agentpool1-38988164-65") + if agentIndex != expectedAgentIndex { + t.Fatalf("incorrect agentIndex. expected=%d actual=%d", expectedAgentIndex, agentIndex) + } + if err != nil { + t.Fatalf("unexpected error: %s", err) + } +} + +func TestGetVMNameIndexWindows(t *testing.T) { + expectedAgentIndex := 20 + + agentIndex, err := GetVMNameIndex(compute.Windows, "38988k8s90320") + if agentIndex != expectedAgentIndex { + t.Fatalf("incorrect agentIndex. expected=%d actual=%d", expectedAgentIndex, agentIndex) + } + if err != nil { + t.Fatalf("unexpected error: %s", err) + } +} diff --git a/cluster-autoscaler/cloudprovider/azure/cluster-autoscaler-standard-master.yaml b/cluster-autoscaler/cloudprovider/azure/cluster-autoscaler-standard-master.yaml new file mode 100644 index 000000000000..fd410de9dd04 --- /dev/null +++ b/cluster-autoscaler/cloudprovider/azure/cluster-autoscaler-standard-master.yaml @@ -0,0 +1,137 @@ +apiVersion: v1 +data: + ClientID: + ClientSecret: + ResourceGroup: + SubscriptionID: + TenantID: + NodeGroup: + Deployment: + APIServerPrivateKey: + CAPrivateKey: + ClientPrivateKey: + KubeConfigPrivateKey: + WindowsAdminPassword: + VMType: c3RhbmRhcmQ= +kind: Secret +metadata: + name: cluster-autoscaler-azure + namespace: kube-system +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + labels: + app: cluster-autoscaler + name: cluster-autoscaler + namespace: kube-system +spec: + replicas: 1 + selector: + matchLabels: + app: cluster-autoscaler + template: + metadata: + labels: + app: cluster-autoscaler + spec: + tolerations: + - effect: NoSchedule + key: node-role.kubernetes.io/master + nodeSelector: + kubernetes.io/role: master + containers: + - command: + - ./cluster-autoscaler + - --v=3 + - --logtostderr=true + - --cloud-provider=azure + - --skip-nodes-with-local-storage=false + - --nodes=1:10:$(ARM_NODE_GROUP) + env: + - name: ARM_SUBSCRIPTION_ID + valueFrom: + secretKeyRef: + key: SubscriptionID + name: cluster-autoscaler-azure + - name: ARM_RESOURCE_GROUP + valueFrom: + secretKeyRef: + key: ResourceGroup + name: cluster-autoscaler-azure + - name: ARM_TENANT_ID + valueFrom: + secretKeyRef: + key: TenantID + name: cluster-autoscaler-azure + - name: ARM_CLIENT_ID + valueFrom: + secretKeyRef: + key: ClientID + name: cluster-autoscaler-azure + - name: ARM_CLIENT_SECRET + valueFrom: + secretKeyRef: + key: ClientSecret + name: cluster-autoscaler-azure + - name: ARM_NODE_GROUP + valueFrom: + secretKeyRef: + key: NodeGroup + name: cluster-autoscaler-azure + - name: ARM_VM_TYPE + valueFrom: + secretKeyRef: + key: VMType + name: cluster-autoscaler-azure + - name: ARM_DEPLOYMENT + valueFrom: + secretKeyRef: + key: Deployment + name: cluster-autoscaler-azure + - name: ARM_APISEVER_PRIVATE_KEY + valueFrom: + secretKeyRef: + key: APIServerPrivateKey + name: cluster-autoscaler-azure + - name: ARM_CA_PRIVATE_KEY + valueFrom: + secretKeyRef: + key: CAPrivateKey + name: cluster-autoscaler-azure + - name: ARM_CLIENT_PRIVATE_KEY + valueFrom: + secretKeyRef: + key: ClientPrivateKey + name: cluster-autoscaler-azure + - name: ARM_KUBECONFIG_PRIVATE_KEY + valueFrom: + secretKeyRef: + key: KubeConfigPrivateKey + name: cluster-autoscaler-azure + - name: ARM_WINDOWS_ADMIN_PASSWORD + valueFrom: + secretKeyRef: + key: WindowsAdminPassword + name: cluster-autoscaler-azure + image: gcr.io/google_containers/cluster-autoscaler:{{ ca_version }} + imagePullPolicy: Always + name: cluster-autoscaler + resources: + limits: + cpu: 100m + memory: 300Mi + requests: + cpu: 100m + memory: 300Mi + volumeMounts: + - mountPath: /etc/ssl/certs/ca-certificates.crt + name: ssl-certs + readOnly: true + dnsPolicy: ClusterFirst + restartPolicy: Always + volumes: + - hostPath: + path: /etc/ssl/certs/ca-certificates.crt + type: "" + name: ssl-certs \ No newline at end of file diff --git a/cluster-autoscaler/cloudprovider/azure/cluster-autoscaler-standard.yaml b/cluster-autoscaler/cloudprovider/azure/cluster-autoscaler-standard.yaml new file mode 100644 index 000000000000..1d45f2c7e516 --- /dev/null +++ b/cluster-autoscaler/cloudprovider/azure/cluster-autoscaler-standard.yaml @@ -0,0 +1,132 @@ +apiVersion: v1 +data: + ClientID: + ClientSecret: + ResourceGroup: + SubscriptionID: + TenantID: + NodeGroup: + Deployment: + APIServerPrivateKey: + CAPrivateKey: + ClientPrivateKey: + KubeConfigPrivateKey: + WindowsAdminPassword: + VMType: c3RhbmRhcmQ= +kind: Secret +metadata: + name: cluster-autoscaler-azure + namespace: kube-system +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + labels: + app: cluster-autoscaler + name: cluster-autoscaler + namespace: kube-system +spec: + replicas: 1 + selector: + matchLabels: + app: cluster-autoscaler + template: + metadata: + labels: + app: cluster-autoscaler + spec: + containers: + - command: + - ./cluster-autoscaler + - --v=3 + - --logtostderr=true + - --cloud-provider=azure + - --skip-nodes-with-local-storage=false + - --nodes=1:10:$(ARM_NODE_GROUP) + env: + - name: ARM_SUBSCRIPTION_ID + valueFrom: + secretKeyRef: + key: SubscriptionID + name: cluster-autoscaler-azure + - name: ARM_RESOURCE_GROUP + valueFrom: + secretKeyRef: + key: ResourceGroup + name: cluster-autoscaler-azure + - name: ARM_TENANT_ID + valueFrom: + secretKeyRef: + key: TenantID + name: cluster-autoscaler-azure + - name: ARM_CLIENT_ID + valueFrom: + secretKeyRef: + key: ClientID + name: cluster-autoscaler-azure + - name: ARM_CLIENT_SECRET + valueFrom: + secretKeyRef: + key: ClientSecret + name: cluster-autoscaler-azure + - name: ARM_NODE_GROUP + valueFrom: + secretKeyRef: + key: NodeGroup + name: cluster-autoscaler-azure + - name: ARM_VM_TYPE + valueFrom: + secretKeyRef: + key: VMType + name: cluster-autoscaler-azure + - name: ARM_DEPLOYMENT + valueFrom: + secretKeyRef: + key: Deployment + name: cluster-autoscaler-azure + - name: ARM_APISEVER_PRIVATE_KEY + valueFrom: + secretKeyRef: + key: APIServerPrivateKey + name: cluster-autoscaler-azure + - name: ARM_CA_PRIVATE_KEY + valueFrom: + secretKeyRef: + key: CAPrivateKey + name: cluster-autoscaler-azure + - name: ARM_CLIENT_PRIVATE_KEY + valueFrom: + secretKeyRef: + key: ClientPrivateKey + name: cluster-autoscaler-azure + - name: ARM_KUBECONFIG_PRIVATE_KEY + valueFrom: + secretKeyRef: + key: KubeConfigPrivateKey + name: cluster-autoscaler-azure + - name: ARM_WINDOWS_ADMIN_PASSWORD + valueFrom: + secretKeyRef: + key: WindowsAdminPassword + name: cluster-autoscaler-azure + image: gcr.io/google_containers/cluster-autoscaler:{{ ca_version }} + imagePullPolicy: Always + name: cluster-autoscaler + resources: + limits: + cpu: 100m + memory: 300Mi + requests: + cpu: 100m + memory: 300Mi + volumeMounts: + - mountPath: /etc/ssl/certs/ca-certificates.crt + name: ssl-certs + readOnly: true + dnsPolicy: ClusterFirst + restartPolicy: Always + volumes: + - hostPath: + path: /etc/ssl/certs/ca-certificates.crt + type: "" + name: ssl-certs \ No newline at end of file diff --git a/cluster-autoscaler/cloudprovider/azure/cluster-autoscaler-vmss-master.yaml b/cluster-autoscaler/cloudprovider/azure/cluster-autoscaler-vmss-master.yaml new file mode 100644 index 000000000000..091b5fcff631 --- /dev/null +++ b/cluster-autoscaler/cloudprovider/azure/cluster-autoscaler-vmss-master.yaml @@ -0,0 +1,100 @@ +apiVersion: v1 +data: + ClientID: + ClientSecret: + ResourceGroup: + SubscriptionID: + TenantID: + NodeGroup: + VMType: dm1zcw== +kind: Secret +metadata: + name: cluster-autoscaler-azure + namespace: kube-system +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + labels: + app: cluster-autoscaler + name: cluster-autoscaler + namespace: kube-system +spec: + replicas: 1 + selector: + matchLabels: + app: cluster-autoscaler + template: + metadata: + labels: + app: cluster-autoscaler + spec: + tolerations: + - effect: NoSchedule + key: node-role.kubernetes.io/master + nodeSelector: + kubernetes.io/role: master + containers: + - command: + - ./cluster-autoscaler + - --v=3 + - --logtostderr=true + - --cloud-provider=azure + - --skip-nodes-with-local-storage=false + - --nodes=1:10:$(ARM_NODE_GROUP) + env: + - name: ARM_SUBSCRIPTION_ID + valueFrom: + secretKeyRef: + key: SubscriptionID + name: cluster-autoscaler-azure + - name: ARM_RESOURCE_GROUP + valueFrom: + secretKeyRef: + key: ResourceGroup + name: cluster-autoscaler-azure + - name: ARM_TENANT_ID + valueFrom: + secretKeyRef: + key: TenantID + name: cluster-autoscaler-azure + - name: ARM_CLIENT_ID + valueFrom: + secretKeyRef: + key: ClientID + name: cluster-autoscaler-azure + - name: ARM_CLIENT_SECRET + valueFrom: + secretKeyRef: + key: ClientSecret + name: cluster-autoscaler-azure + - name: ARM_NODE_GROUP + valueFrom: + secretKeyRef: + key: NodeGroup + name: cluster-autoscaler-azure + - name: ARM_VM_TYPE + valueFrom: + secretKeyRef: + key: VMType + name: cluster-autoscaler-azure + - image: gcr.io/google_containers/cluster-autoscaler:{{ ca_version }} + imagePullPolicy: Always + name: cluster-autoscaler + resources: + limits: + cpu: 100m + memory: 300Mi + requests: + cpu: 100m + memory: 300Mi + volumeMounts: + - mountPath: /etc/ssl/certs/ca-certificates.crt + name: ssl-certs + readOnly: true + restartPolicy: Always + volumes: + - hostPath: + path: /etc/ssl/certs/ca-certificates.crt + type: "" + name: ssl-certs \ No newline at end of file diff --git a/cluster-autoscaler/cloudprovider/azure/cluster-autoscaler-vmss.yaml b/cluster-autoscaler/cloudprovider/azure/cluster-autoscaler-vmss.yaml new file mode 100644 index 000000000000..5ec499d74629 --- /dev/null +++ b/cluster-autoscaler/cloudprovider/azure/cluster-autoscaler-vmss.yaml @@ -0,0 +1,95 @@ +apiVersion: v1 +data: + ClientID: + ClientSecret: + ResourceGroup: + SubscriptionID: + TenantID: + NodeGroup: + VMType: dm1zcw== +kind: Secret +metadata: + name: cluster-autoscaler-azure + namespace: kube-system +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + labels: + app: cluster-autoscaler + name: cluster-autoscaler + namespace: kube-system +spec: + replicas: 1 + selector: + matchLabels: + app: cluster-autoscaler + template: + metadata: + labels: + app: cluster-autoscaler + spec: + containers: + - command: + - ./cluster-autoscaler + - --v=3 + - --logtostderr=true + - --cloud-provider=azure + - --skip-nodes-with-local-storage=false + - --nodes=1:10:$(ARM_NODE_GROUP) + env: + - name: ARM_SUBSCRIPTION_ID + valueFrom: + secretKeyRef: + key: SubscriptionID + name: cluster-autoscaler-azure + - name: ARM_RESOURCE_GROUP + valueFrom: + secretKeyRef: + key: ResourceGroup + name: cluster-autoscaler-azure + - name: ARM_TENANT_ID + valueFrom: + secretKeyRef: + key: TenantID + name: cluster-autoscaler-azure + - name: ARM_CLIENT_ID + valueFrom: + secretKeyRef: + key: ClientID + name: cluster-autoscaler-azure + - name: ARM_CLIENT_SECRET + valueFrom: + secretKeyRef: + key: ClientSecret + name: cluster-autoscaler-azure + - name: ARM_NODE_GROUP + valueFrom: + secretKeyRef: + key: NodeGroup + name: cluster-autoscaler-azure + - name: ARM_VM_TYPE + valueFrom: + secretKeyRef: + key: VMType + name: cluster-autoscaler-azure + - image: gcr.io/google_containers/cluster-autoscaler:{{ ca_version }} + imagePullPolicy: Always + name: cluster-autoscaler + resources: + limits: + cpu: 100m + memory: 300Mi + requests: + cpu: 100m + memory: 300Mi + volumeMounts: + - mountPath: /etc/ssl/certs/ca-certificates.crt + name: ssl-certs + readOnly: true + restartPolicy: Always + volumes: + - hostPath: + path: /etc/ssl/certs/ca-certificates.crt + type: "" + name: ssl-certs \ No newline at end of file diff --git a/cluster-autoscaler/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/resources/client.go b/cluster-autoscaler/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/resources/client.go new file mode 100755 index 000000000000..348e0cf16b6e --- /dev/null +++ b/cluster-autoscaler/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/resources/client.go @@ -0,0 +1,51 @@ +// Package resources implements the Azure ARM Resources service API version 2017-05-10. +// +// Provides operations for working with resources and resource groups. +package resources + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/Azure/go-autorest/autorest" +) + +const ( + // DefaultBaseURI is the default URI used for the service Resources + DefaultBaseURI = "https://management.azure.com" +) + +// ManagementClient is the base client for Resources. +type ManagementClient struct { + autorest.Client + BaseURI string + SubscriptionID string +} + +// New creates an instance of the ManagementClient client. +func New(subscriptionID string) ManagementClient { + return NewWithBaseURI(DefaultBaseURI, subscriptionID) +} + +// NewWithBaseURI creates an instance of the ManagementClient client. +func NewWithBaseURI(baseURI string, subscriptionID string) ManagementClient { + return ManagementClient{ + Client: autorest.NewClientWithUserAgent(UserAgent()), + BaseURI: baseURI, + SubscriptionID: subscriptionID, + } +} diff --git a/cluster-autoscaler/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/resources/deploymentoperations.go b/cluster-autoscaler/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/resources/deploymentoperations.go new file mode 100755 index 000000000000..116c8b728e06 --- /dev/null +++ b/cluster-autoscaler/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/resources/deploymentoperations.go @@ -0,0 +1,269 @@ +package resources + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/azure" + "github.com/Azure/go-autorest/autorest/validation" + "net/http" +) + +// DeploymentOperationsClient is the provides operations for working with resources and resource groups. +type DeploymentOperationsClient struct { + ManagementClient +} + +// NewDeploymentOperationsClient creates an instance of the DeploymentOperationsClient client. +func NewDeploymentOperationsClient(subscriptionID string) DeploymentOperationsClient { + return NewDeploymentOperationsClientWithBaseURI(DefaultBaseURI, subscriptionID) +} + +// NewDeploymentOperationsClientWithBaseURI creates an instance of the DeploymentOperationsClient client. +func NewDeploymentOperationsClientWithBaseURI(baseURI string, subscriptionID string) DeploymentOperationsClient { + return DeploymentOperationsClient{NewWithBaseURI(baseURI, subscriptionID)} +} + +// Get gets a deployments operation. +// +// resourceGroupName is the name of the resource group. The name is case insensitive. deploymentName is the name of the +// deployment. operationID is the ID of the operation to get. +func (client DeploymentOperationsClient) Get(resourceGroupName string, deploymentName string, operationID string) (result DeploymentOperation, err error) { + if err := validation.Validate([]validation.Validation{ + {TargetValue: resourceGroupName, + Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil}, + {Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}, + {TargetValue: deploymentName, + Constraints: []validation.Constraint{{Target: "deploymentName", Name: validation.MaxLength, Rule: 64, Chain: nil}, + {Target: "deploymentName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "deploymentName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}}); err != nil { + return result, validation.NewErrorWithValidationError(err, "resources.DeploymentOperationsClient", "Get") + } + + req, err := client.GetPreparer(resourceGroupName, deploymentName, operationID) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.DeploymentOperationsClient", "Get", nil, "Failure preparing request") + return + } + + resp, err := client.GetSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "resources.DeploymentOperationsClient", "Get", resp, "Failure sending request") + return + } + + result, err = client.GetResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.DeploymentOperationsClient", "Get", resp, "Failure responding to request") + } + + return +} + +// GetPreparer prepares the Get request. +func (client DeploymentOperationsClient) GetPreparer(resourceGroupName string, deploymentName string, operationID string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "deploymentName": autorest.Encode("path", deploymentName), + "operationId": autorest.Encode("path", operationID), + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/deployments/{deploymentName}/operations/{operationId}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// GetSender sends the Get request. The method will close the +// http.Response Body if it receives an error. +func (client DeploymentOperationsClient) GetSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// GetResponder handles the response to the Get request. The method always +// closes the http.Response Body. +func (client DeploymentOperationsClient) GetResponder(resp *http.Response) (result DeploymentOperation, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// List gets all deployments operations for a deployment. +// +// resourceGroupName is the name of the resource group. The name is case insensitive. deploymentName is the name of the +// deployment with the operation to get. top is the number of results to return. +func (client DeploymentOperationsClient) List(resourceGroupName string, deploymentName string, top *int32) (result DeploymentOperationsListResult, err error) { + if err := validation.Validate([]validation.Validation{ + {TargetValue: resourceGroupName, + Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil}, + {Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}, + {TargetValue: deploymentName, + Constraints: []validation.Constraint{{Target: "deploymentName", Name: validation.MaxLength, Rule: 64, Chain: nil}, + {Target: "deploymentName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "deploymentName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}}); err != nil { + return result, validation.NewErrorWithValidationError(err, "resources.DeploymentOperationsClient", "List") + } + + req, err := client.ListPreparer(resourceGroupName, deploymentName, top) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.DeploymentOperationsClient", "List", nil, "Failure preparing request") + return + } + + resp, err := client.ListSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "resources.DeploymentOperationsClient", "List", resp, "Failure sending request") + return + } + + result, err = client.ListResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.DeploymentOperationsClient", "List", resp, "Failure responding to request") + } + + return +} + +// ListPreparer prepares the List request. +func (client DeploymentOperationsClient) ListPreparer(resourceGroupName string, deploymentName string, top *int32) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "deploymentName": autorest.Encode("path", deploymentName), + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + if top != nil { + queryParameters["$top"] = autorest.Encode("query", *top) + } + + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/deployments/{deploymentName}/operations", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// ListSender sends the List request. The method will close the +// http.Response Body if it receives an error. +func (client DeploymentOperationsClient) ListSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// ListResponder handles the response to the List request. The method always +// closes the http.Response Body. +func (client DeploymentOperationsClient) ListResponder(resp *http.Response) (result DeploymentOperationsListResult, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// ListNextResults retrieves the next set of results, if any. +func (client DeploymentOperationsClient) ListNextResults(lastResults DeploymentOperationsListResult) (result DeploymentOperationsListResult, err error) { + req, err := lastResults.DeploymentOperationsListResultPreparer() + if err != nil { + return result, autorest.NewErrorWithError(err, "resources.DeploymentOperationsClient", "List", nil, "Failure preparing next results request") + } + if req == nil { + return + } + + resp, err := client.ListSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + return result, autorest.NewErrorWithError(err, "resources.DeploymentOperationsClient", "List", resp, "Failure sending next results request") + } + + result, err = client.ListResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.DeploymentOperationsClient", "List", resp, "Failure responding to next results request") + } + + return +} + +// ListComplete gets all elements from the list without paging. +func (client DeploymentOperationsClient) ListComplete(resourceGroupName string, deploymentName string, top *int32, cancel <-chan struct{}) (<-chan DeploymentOperation, <-chan error) { + resultChan := make(chan DeploymentOperation) + errChan := make(chan error, 1) + go func() { + defer func() { + close(resultChan) + close(errChan) + }() + list, err := client.List(resourceGroupName, deploymentName, top) + if err != nil { + errChan <- err + return + } + if list.Value != nil { + for _, item := range *list.Value { + select { + case <-cancel: + return + case resultChan <- item: + // Intentionally left blank + } + } + } + for list.NextLink != nil { + list, err = client.ListNextResults(list) + if err != nil { + errChan <- err + return + } + if list.Value != nil { + for _, item := range *list.Value { + select { + case <-cancel: + return + case resultChan <- item: + // Intentionally left blank + } + } + } + } + }() + return resultChan, errChan +} diff --git a/cluster-autoscaler/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/resources/deployments.go b/cluster-autoscaler/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/resources/deployments.go new file mode 100755 index 000000000000..9941f6096115 --- /dev/null +++ b/cluster-autoscaler/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/resources/deployments.go @@ -0,0 +1,799 @@ +package resources + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/azure" + "github.com/Azure/go-autorest/autorest/validation" + "net/http" +) + +// DeploymentsClient is the provides operations for working with resources and resource groups. +type DeploymentsClient struct { + ManagementClient +} + +// NewDeploymentsClient creates an instance of the DeploymentsClient client. +func NewDeploymentsClient(subscriptionID string) DeploymentsClient { + return NewDeploymentsClientWithBaseURI(DefaultBaseURI, subscriptionID) +} + +// NewDeploymentsClientWithBaseURI creates an instance of the DeploymentsClient client. +func NewDeploymentsClientWithBaseURI(baseURI string, subscriptionID string) DeploymentsClient { + return DeploymentsClient{NewWithBaseURI(baseURI, subscriptionID)} +} + +// Cancel you can cancel a deployment only if the provisioningState is Accepted or Running. After the deployment is +// canceled, the provisioningState is set to Canceled. Canceling a template deployment stops the currently running +// template deployment and leaves the resource group partially deployed. +// +// resourceGroupName is the name of the resource group. The name is case insensitive. deploymentName is the name of the +// deployment to cancel. +func (client DeploymentsClient) Cancel(resourceGroupName string, deploymentName string) (result autorest.Response, err error) { + if err := validation.Validate([]validation.Validation{ + {TargetValue: resourceGroupName, + Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil}, + {Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}, + {TargetValue: deploymentName, + Constraints: []validation.Constraint{{Target: "deploymentName", Name: validation.MaxLength, Rule: 64, Chain: nil}, + {Target: "deploymentName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "deploymentName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}}); err != nil { + return result, validation.NewErrorWithValidationError(err, "resources.DeploymentsClient", "Cancel") + } + + req, err := client.CancelPreparer(resourceGroupName, deploymentName) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.DeploymentsClient", "Cancel", nil, "Failure preparing request") + return + } + + resp, err := client.CancelSender(req) + if err != nil { + result.Response = resp + err = autorest.NewErrorWithError(err, "resources.DeploymentsClient", "Cancel", resp, "Failure sending request") + return + } + + result, err = client.CancelResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.DeploymentsClient", "Cancel", resp, "Failure responding to request") + } + + return +} + +// CancelPreparer prepares the Cancel request. +func (client DeploymentsClient) CancelPreparer(resourceGroupName string, deploymentName string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "deploymentName": autorest.Encode("path", deploymentName), + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsPost(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Resources/deployments/{deploymentName}/cancel", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// CancelSender sends the Cancel request. The method will close the +// http.Response Body if it receives an error. +func (client DeploymentsClient) CancelSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// CancelResponder handles the response to the Cancel request. The method always +// closes the http.Response Body. +func (client DeploymentsClient) CancelResponder(resp *http.Response) (result autorest.Response, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusNoContent), + autorest.ByClosing()) + result.Response = resp + return +} + +// CheckExistence checks whether the deployment exists. +// +// resourceGroupName is the name of the resource group with the deployment to check. The name is case insensitive. +// deploymentName is the name of the deployment to check. +func (client DeploymentsClient) CheckExistence(resourceGroupName string, deploymentName string) (result autorest.Response, err error) { + if err := validation.Validate([]validation.Validation{ + {TargetValue: resourceGroupName, + Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil}, + {Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}, + {TargetValue: deploymentName, + Constraints: []validation.Constraint{{Target: "deploymentName", Name: validation.MaxLength, Rule: 64, Chain: nil}, + {Target: "deploymentName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "deploymentName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}}); err != nil { + return result, validation.NewErrorWithValidationError(err, "resources.DeploymentsClient", "CheckExistence") + } + + req, err := client.CheckExistencePreparer(resourceGroupName, deploymentName) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.DeploymentsClient", "CheckExistence", nil, "Failure preparing request") + return + } + + resp, err := client.CheckExistenceSender(req) + if err != nil { + result.Response = resp + err = autorest.NewErrorWithError(err, "resources.DeploymentsClient", "CheckExistence", resp, "Failure sending request") + return + } + + result, err = client.CheckExistenceResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.DeploymentsClient", "CheckExistence", resp, "Failure responding to request") + } + + return +} + +// CheckExistencePreparer prepares the CheckExistence request. +func (client DeploymentsClient) CheckExistencePreparer(resourceGroupName string, deploymentName string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "deploymentName": autorest.Encode("path", deploymentName), + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsHead(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Resources/deployments/{deploymentName}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// CheckExistenceSender sends the CheckExistence request. The method will close the +// http.Response Body if it receives an error. +func (client DeploymentsClient) CheckExistenceSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// CheckExistenceResponder handles the response to the CheckExistence request. The method always +// closes the http.Response Body. +func (client DeploymentsClient) CheckExistenceResponder(resp *http.Response) (result autorest.Response, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusNoContent, http.StatusNotFound), + autorest.ByClosing()) + result.Response = resp + return +} + +// CreateOrUpdate you can provide the template and parameters directly in the request or link to JSON files. This +// method may poll for completion. Polling can be canceled by passing the cancel channel argument. The channel will be +// used to cancel polling and any outstanding HTTP requests. +// +// resourceGroupName is the name of the resource group to deploy the resources to. The name is case insensitive. The +// resource group must already exist. deploymentName is the name of the deployment. parameters is additional parameters +// supplied to the operation. +func (client DeploymentsClient) CreateOrUpdate(resourceGroupName string, deploymentName string, parameters Deployment, cancel <-chan struct{}) (<-chan DeploymentExtended, <-chan error) { + resultChan := make(chan DeploymentExtended, 1) + errChan := make(chan error, 1) + if err := validation.Validate([]validation.Validation{ + {TargetValue: resourceGroupName, + Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil}, + {Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}, + {TargetValue: deploymentName, + Constraints: []validation.Constraint{{Target: "deploymentName", Name: validation.MaxLength, Rule: 64, Chain: nil}, + {Target: "deploymentName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "deploymentName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}, + {TargetValue: parameters, + Constraints: []validation.Constraint{{Target: "parameters.Properties", Name: validation.Null, Rule: true, + Chain: []validation.Constraint{{Target: "parameters.Properties.TemplateLink", Name: validation.Null, Rule: false, + Chain: []validation.Constraint{{Target: "parameters.Properties.TemplateLink.URI", Name: validation.Null, Rule: true, Chain: nil}}}, + {Target: "parameters.Properties.ParametersLink", Name: validation.Null, Rule: false, + Chain: []validation.Constraint{{Target: "parameters.Properties.ParametersLink.URI", Name: validation.Null, Rule: true, Chain: nil}}}, + }}}}}); err != nil { + errChan <- validation.NewErrorWithValidationError(err, "resources.DeploymentsClient", "CreateOrUpdate") + close(errChan) + close(resultChan) + return resultChan, errChan + } + + go func() { + var err error + var result DeploymentExtended + defer func() { + if err != nil { + errChan <- err + } + resultChan <- result + close(resultChan) + close(errChan) + }() + req, err := client.CreateOrUpdatePreparer(resourceGroupName, deploymentName, parameters, cancel) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.DeploymentsClient", "CreateOrUpdate", nil, "Failure preparing request") + return + } + + resp, err := client.CreateOrUpdateSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "resources.DeploymentsClient", "CreateOrUpdate", resp, "Failure sending request") + return + } + + result, err = client.CreateOrUpdateResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.DeploymentsClient", "CreateOrUpdate", resp, "Failure responding to request") + } + }() + return resultChan, errChan +} + +// CreateOrUpdatePreparer prepares the CreateOrUpdate request. +func (client DeploymentsClient) CreateOrUpdatePreparer(resourceGroupName string, deploymentName string, parameters Deployment, cancel <-chan struct{}) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "deploymentName": autorest.Encode("path", deploymentName), + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsJSON(), + autorest.AsPut(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Resources/deployments/{deploymentName}", pathParameters), + autorest.WithJSON(parameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{Cancel: cancel}) +} + +// CreateOrUpdateSender sends the CreateOrUpdate request. The method will close the +// http.Response Body if it receives an error. +func (client DeploymentsClient) CreateOrUpdateSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, + req, + azure.DoPollForAsynchronous(client.PollingDelay)) +} + +// CreateOrUpdateResponder handles the response to the CreateOrUpdate request. The method always +// closes the http.Response Body. +func (client DeploymentsClient) CreateOrUpdateResponder(resp *http.Response) (result DeploymentExtended, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusCreated), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// Delete a template deployment that is currently running cannot be deleted. Deleting a template deployment removes the +// associated deployment operations. Deleting a template deployment does not affect the state of the resource group. +// This is an asynchronous operation that returns a status of 202 until the template deployment is successfully +// deleted. The Location response header contains the URI that is used to obtain the status of the process. While the +// process is running, a call to the URI in the Location header returns a status of 202. When the process finishes, the +// URI in the Location header returns a status of 204 on success. If the asynchronous request failed, the URI in the +// Location header returns an error-level status code. This method may poll for completion. Polling can be canceled by +// passing the cancel channel argument. The channel will be used to cancel polling and any outstanding HTTP requests. +// +// resourceGroupName is the name of the resource group with the deployment to delete. The name is case insensitive. +// deploymentName is the name of the deployment to delete. +func (client DeploymentsClient) Delete(resourceGroupName string, deploymentName string, cancel <-chan struct{}) (<-chan autorest.Response, <-chan error) { + resultChan := make(chan autorest.Response, 1) + errChan := make(chan error, 1) + if err := validation.Validate([]validation.Validation{ + {TargetValue: resourceGroupName, + Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil}, + {Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}, + {TargetValue: deploymentName, + Constraints: []validation.Constraint{{Target: "deploymentName", Name: validation.MaxLength, Rule: 64, Chain: nil}, + {Target: "deploymentName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "deploymentName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}}); err != nil { + errChan <- validation.NewErrorWithValidationError(err, "resources.DeploymentsClient", "Delete") + close(errChan) + close(resultChan) + return resultChan, errChan + } + + go func() { + var err error + var result autorest.Response + defer func() { + if err != nil { + errChan <- err + } + resultChan <- result + close(resultChan) + close(errChan) + }() + req, err := client.DeletePreparer(resourceGroupName, deploymentName, cancel) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.DeploymentsClient", "Delete", nil, "Failure preparing request") + return + } + + resp, err := client.DeleteSender(req) + if err != nil { + result.Response = resp + err = autorest.NewErrorWithError(err, "resources.DeploymentsClient", "Delete", resp, "Failure sending request") + return + } + + result, err = client.DeleteResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.DeploymentsClient", "Delete", resp, "Failure responding to request") + } + }() + return resultChan, errChan +} + +// DeletePreparer prepares the Delete request. +func (client DeploymentsClient) DeletePreparer(resourceGroupName string, deploymentName string, cancel <-chan struct{}) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "deploymentName": autorest.Encode("path", deploymentName), + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsDelete(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Resources/deployments/{deploymentName}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{Cancel: cancel}) +} + +// DeleteSender sends the Delete request. The method will close the +// http.Response Body if it receives an error. +func (client DeploymentsClient) DeleteSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, + req, + azure.DoPollForAsynchronous(client.PollingDelay)) +} + +// DeleteResponder handles the response to the Delete request. The method always +// closes the http.Response Body. +func (client DeploymentsClient) DeleteResponder(resp *http.Response) (result autorest.Response, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted, http.StatusNoContent), + autorest.ByClosing()) + result.Response = resp + return +} + +// ExportTemplate exports the template used for specified deployment. +// +// resourceGroupName is the name of the resource group. The name is case insensitive. deploymentName is the name of the +// deployment from which to get the template. +func (client DeploymentsClient) ExportTemplate(resourceGroupName string, deploymentName string) (result DeploymentExportResult, err error) { + if err := validation.Validate([]validation.Validation{ + {TargetValue: resourceGroupName, + Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil}, + {Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}, + {TargetValue: deploymentName, + Constraints: []validation.Constraint{{Target: "deploymentName", Name: validation.MaxLength, Rule: 64, Chain: nil}, + {Target: "deploymentName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "deploymentName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}}); err != nil { + return result, validation.NewErrorWithValidationError(err, "resources.DeploymentsClient", "ExportTemplate") + } + + req, err := client.ExportTemplatePreparer(resourceGroupName, deploymentName) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.DeploymentsClient", "ExportTemplate", nil, "Failure preparing request") + return + } + + resp, err := client.ExportTemplateSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "resources.DeploymentsClient", "ExportTemplate", resp, "Failure sending request") + return + } + + result, err = client.ExportTemplateResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.DeploymentsClient", "ExportTemplate", resp, "Failure responding to request") + } + + return +} + +// ExportTemplatePreparer prepares the ExportTemplate request. +func (client DeploymentsClient) ExportTemplatePreparer(resourceGroupName string, deploymentName string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "deploymentName": autorest.Encode("path", deploymentName), + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsPost(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Resources/deployments/{deploymentName}/exportTemplate", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// ExportTemplateSender sends the ExportTemplate request. The method will close the +// http.Response Body if it receives an error. +func (client DeploymentsClient) ExportTemplateSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// ExportTemplateResponder handles the response to the ExportTemplate request. The method always +// closes the http.Response Body. +func (client DeploymentsClient) ExportTemplateResponder(resp *http.Response) (result DeploymentExportResult, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// Get gets a deployment. +// +// resourceGroupName is the name of the resource group. The name is case insensitive. deploymentName is the name of the +// deployment to get. +func (client DeploymentsClient) Get(resourceGroupName string, deploymentName string) (result DeploymentExtended, err error) { + if err := validation.Validate([]validation.Validation{ + {TargetValue: resourceGroupName, + Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil}, + {Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}, + {TargetValue: deploymentName, + Constraints: []validation.Constraint{{Target: "deploymentName", Name: validation.MaxLength, Rule: 64, Chain: nil}, + {Target: "deploymentName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "deploymentName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}}); err != nil { + return result, validation.NewErrorWithValidationError(err, "resources.DeploymentsClient", "Get") + } + + req, err := client.GetPreparer(resourceGroupName, deploymentName) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.DeploymentsClient", "Get", nil, "Failure preparing request") + return + } + + resp, err := client.GetSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "resources.DeploymentsClient", "Get", resp, "Failure sending request") + return + } + + result, err = client.GetResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.DeploymentsClient", "Get", resp, "Failure responding to request") + } + + return +} + +// GetPreparer prepares the Get request. +func (client DeploymentsClient) GetPreparer(resourceGroupName string, deploymentName string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "deploymentName": autorest.Encode("path", deploymentName), + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Resources/deployments/{deploymentName}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// GetSender sends the Get request. The method will close the +// http.Response Body if it receives an error. +func (client DeploymentsClient) GetSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// GetResponder handles the response to the Get request. The method always +// closes the http.Response Body. +func (client DeploymentsClient) GetResponder(resp *http.Response) (result DeploymentExtended, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// ListByResourceGroup get all the deployments for a resource group. +// +// resourceGroupName is the name of the resource group with the deployments to get. The name is case insensitive. +// filter is the filter to apply on the operation. For example, you can use $filter=provisioningState eq '{state}'. top +// is the number of results to get. If null is passed, returns all deployments. +func (client DeploymentsClient) ListByResourceGroup(resourceGroupName string, filter string, top *int32) (result DeploymentListResult, err error) { + if err := validation.Validate([]validation.Validation{ + {TargetValue: resourceGroupName, + Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil}, + {Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}}); err != nil { + return result, validation.NewErrorWithValidationError(err, "resources.DeploymentsClient", "ListByResourceGroup") + } + + req, err := client.ListByResourceGroupPreparer(resourceGroupName, filter, top) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.DeploymentsClient", "ListByResourceGroup", nil, "Failure preparing request") + return + } + + resp, err := client.ListByResourceGroupSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "resources.DeploymentsClient", "ListByResourceGroup", resp, "Failure sending request") + return + } + + result, err = client.ListByResourceGroupResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.DeploymentsClient", "ListByResourceGroup", resp, "Failure responding to request") + } + + return +} + +// ListByResourceGroupPreparer prepares the ListByResourceGroup request. +func (client DeploymentsClient) ListByResourceGroupPreparer(resourceGroupName string, filter string, top *int32) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + if len(filter) > 0 { + queryParameters["$filter"] = autorest.Encode("query", filter) + } + if top != nil { + queryParameters["$top"] = autorest.Encode("query", *top) + } + + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Resources/deployments/", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// ListByResourceGroupSender sends the ListByResourceGroup request. The method will close the +// http.Response Body if it receives an error. +func (client DeploymentsClient) ListByResourceGroupSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// ListByResourceGroupResponder handles the response to the ListByResourceGroup request. The method always +// closes the http.Response Body. +func (client DeploymentsClient) ListByResourceGroupResponder(resp *http.Response) (result DeploymentListResult, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// ListByResourceGroupNextResults retrieves the next set of results, if any. +func (client DeploymentsClient) ListByResourceGroupNextResults(lastResults DeploymentListResult) (result DeploymentListResult, err error) { + req, err := lastResults.DeploymentListResultPreparer() + if err != nil { + return result, autorest.NewErrorWithError(err, "resources.DeploymentsClient", "ListByResourceGroup", nil, "Failure preparing next results request") + } + if req == nil { + return + } + + resp, err := client.ListByResourceGroupSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + return result, autorest.NewErrorWithError(err, "resources.DeploymentsClient", "ListByResourceGroup", resp, "Failure sending next results request") + } + + result, err = client.ListByResourceGroupResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.DeploymentsClient", "ListByResourceGroup", resp, "Failure responding to next results request") + } + + return +} + +// ListByResourceGroupComplete gets all elements from the list without paging. +func (client DeploymentsClient) ListByResourceGroupComplete(resourceGroupName string, filter string, top *int32, cancel <-chan struct{}) (<-chan DeploymentExtended, <-chan error) { + resultChan := make(chan DeploymentExtended) + errChan := make(chan error, 1) + go func() { + defer func() { + close(resultChan) + close(errChan) + }() + list, err := client.ListByResourceGroup(resourceGroupName, filter, top) + if err != nil { + errChan <- err + return + } + if list.Value != nil { + for _, item := range *list.Value { + select { + case <-cancel: + return + case resultChan <- item: + // Intentionally left blank + } + } + } + for list.NextLink != nil { + list, err = client.ListByResourceGroupNextResults(list) + if err != nil { + errChan <- err + return + } + if list.Value != nil { + for _, item := range *list.Value { + select { + case <-cancel: + return + case resultChan <- item: + // Intentionally left blank + } + } + } + } + }() + return resultChan, errChan +} + +// Validate validates whether the specified template is syntactically correct and will be accepted by Azure Resource +// Manager.. +// +// resourceGroupName is the name of the resource group the template will be deployed to. The name is case insensitive. +// deploymentName is the name of the deployment. parameters is parameters to validate. +func (client DeploymentsClient) Validate(resourceGroupName string, deploymentName string, parameters Deployment) (result DeploymentValidateResult, err error) { + if err := validation.Validate([]validation.Validation{ + {TargetValue: resourceGroupName, + Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil}, + {Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}, + {TargetValue: deploymentName, + Constraints: []validation.Constraint{{Target: "deploymentName", Name: validation.MaxLength, Rule: 64, Chain: nil}, + {Target: "deploymentName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "deploymentName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}, + {TargetValue: parameters, + Constraints: []validation.Constraint{{Target: "parameters.Properties", Name: validation.Null, Rule: true, + Chain: []validation.Constraint{{Target: "parameters.Properties.TemplateLink", Name: validation.Null, Rule: false, + Chain: []validation.Constraint{{Target: "parameters.Properties.TemplateLink.URI", Name: validation.Null, Rule: true, Chain: nil}}}, + {Target: "parameters.Properties.ParametersLink", Name: validation.Null, Rule: false, + Chain: []validation.Constraint{{Target: "parameters.Properties.ParametersLink.URI", Name: validation.Null, Rule: true, Chain: nil}}}, + }}}}}); err != nil { + return result, validation.NewErrorWithValidationError(err, "resources.DeploymentsClient", "Validate") + } + + req, err := client.ValidatePreparer(resourceGroupName, deploymentName, parameters) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.DeploymentsClient", "Validate", nil, "Failure preparing request") + return + } + + resp, err := client.ValidateSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "resources.DeploymentsClient", "Validate", resp, "Failure sending request") + return + } + + result, err = client.ValidateResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.DeploymentsClient", "Validate", resp, "Failure responding to request") + } + + return +} + +// ValidatePreparer prepares the Validate request. +func (client DeploymentsClient) ValidatePreparer(resourceGroupName string, deploymentName string, parameters Deployment) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "deploymentName": autorest.Encode("path", deploymentName), + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsJSON(), + autorest.AsPost(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Resources/deployments/{deploymentName}/validate", pathParameters), + autorest.WithJSON(parameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// ValidateSender sends the Validate request. The method will close the +// http.Response Body if it receives an error. +func (client DeploymentsClient) ValidateSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// ValidateResponder handles the response to the Validate request. The method always +// closes the http.Response Body. +func (client DeploymentsClient) ValidateResponder(resp *http.Response) (result DeploymentValidateResult, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusBadRequest), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} diff --git a/cluster-autoscaler/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/resources/group.go b/cluster-autoscaler/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/resources/group.go new file mode 100644 index 000000000000..2eac7ac35478 --- /dev/null +++ b/cluster-autoscaler/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/resources/group.go @@ -0,0 +1,1181 @@ +package resources + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/azure" + "github.com/Azure/go-autorest/autorest/validation" + "net/http" +) + +// GroupClient is the provides operations for working with resources and resource groups. +type GroupClient struct { + ManagementClient +} + +// NewGroupClient creates an instance of the GroupClient client. +func NewGroupClient(subscriptionID string) GroupClient { + return NewGroupClientWithBaseURI(DefaultBaseURI, subscriptionID) +} + +// NewGroupClientWithBaseURI creates an instance of the GroupClient client. +func NewGroupClientWithBaseURI(baseURI string, subscriptionID string) GroupClient { + return GroupClient{NewWithBaseURI(baseURI, subscriptionID)} +} + +// CheckExistence checks whether a resource exists. +// +// resourceGroupName is the name of the resource group containing the resource to check. The name is case insensitive. +// resourceProviderNamespace is the resource provider of the resource to check. parentResourcePath is the parent +// resource identity. resourceType is the resource type. resourceName is the name of the resource to check whether it +// exists. +func (client GroupClient) CheckExistence(resourceGroupName string, resourceProviderNamespace string, parentResourcePath string, resourceType string, resourceName string) (result autorest.Response, err error) { + if err := validation.Validate([]validation.Validation{ + {TargetValue: resourceGroupName, + Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil}, + {Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}}); err != nil { + return result, validation.NewErrorWithValidationError(err, "resources.GroupClient", "CheckExistence") + } + + req, err := client.CheckExistencePreparer(resourceGroupName, resourceProviderNamespace, parentResourcePath, resourceType, resourceName) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupClient", "CheckExistence", nil, "Failure preparing request") + return + } + + resp, err := client.CheckExistenceSender(req) + if err != nil { + result.Response = resp + err = autorest.NewErrorWithError(err, "resources.GroupClient", "CheckExistence", resp, "Failure sending request") + return + } + + result, err = client.CheckExistenceResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupClient", "CheckExistence", resp, "Failure responding to request") + } + + return +} + +// CheckExistencePreparer prepares the CheckExistence request. +func (client GroupClient) CheckExistencePreparer(resourceGroupName string, resourceProviderNamespace string, parentResourcePath string, resourceType string, resourceName string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "parentResourcePath": parentResourcePath, + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "resourceName": autorest.Encode("path", resourceName), + "resourceProviderNamespace": autorest.Encode("path", resourceProviderNamespace), + "resourceType": resourceType, + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsHead(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{parentResourcePath}/{resourceType}/{resourceName}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// CheckExistenceSender sends the CheckExistence request. The method will close the +// http.Response Body if it receives an error. +func (client GroupClient) CheckExistenceSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// CheckExistenceResponder handles the response to the CheckExistence request. The method always +// closes the http.Response Body. +func (client GroupClient) CheckExistenceResponder(resp *http.Response) (result autorest.Response, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusNoContent, http.StatusNotFound), + autorest.ByClosing()) + result.Response = resp + return +} + +// CheckExistenceByID checks by ID whether a resource exists. +// +// resourceID is the fully qualified ID of the resource, including the resource name and resource type. Use the format, +// /subscriptions/{guid}/resourceGroups/{resource-group-name}/{resource-provider-namespace}/{resource-type}/{resource-name} +func (client GroupClient) CheckExistenceByID(resourceID string) (result autorest.Response, err error) { + req, err := client.CheckExistenceByIDPreparer(resourceID) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupClient", "CheckExistenceByID", nil, "Failure preparing request") + return + } + + resp, err := client.CheckExistenceByIDSender(req) + if err != nil { + result.Response = resp + err = autorest.NewErrorWithError(err, "resources.GroupClient", "CheckExistenceByID", resp, "Failure sending request") + return + } + + result, err = client.CheckExistenceByIDResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupClient", "CheckExistenceByID", resp, "Failure responding to request") + } + + return +} + +// CheckExistenceByIDPreparer prepares the CheckExistenceByID request. +func (client GroupClient) CheckExistenceByIDPreparer(resourceID string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "resourceId": resourceID, + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsHead(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/{resourceId}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// CheckExistenceByIDSender sends the CheckExistenceByID request. The method will close the +// http.Response Body if it receives an error. +func (client GroupClient) CheckExistenceByIDSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// CheckExistenceByIDResponder handles the response to the CheckExistenceByID request. The method always +// closes the http.Response Body. +func (client GroupClient) CheckExistenceByIDResponder(resp *http.Response) (result autorest.Response, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusNoContent, http.StatusNotFound), + autorest.ByClosing()) + result.Response = resp + return +} + +// CreateOrUpdate creates a resource. This method may poll for completion. Polling can be canceled by passing the +// cancel channel argument. The channel will be used to cancel polling and any outstanding HTTP requests. +// +// resourceGroupName is the name of the resource group for the resource. The name is case insensitive. +// resourceProviderNamespace is the namespace of the resource provider. parentResourcePath is the parent resource +// identity. resourceType is the resource type of the resource to create. resourceName is the name of the resource to +// create. parameters is parameters for creating or updating the resource. +func (client GroupClient) CreateOrUpdate(resourceGroupName string, resourceProviderNamespace string, parentResourcePath string, resourceType string, resourceName string, parameters GenericResource, cancel <-chan struct{}) (<-chan GenericResource, <-chan error) { + resultChan := make(chan GenericResource, 1) + errChan := make(chan error, 1) + if err := validation.Validate([]validation.Validation{ + {TargetValue: resourceGroupName, + Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil}, + {Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}, + {TargetValue: parameters, + Constraints: []validation.Constraint{{Target: "parameters.Kind", Name: validation.Null, Rule: false, + Chain: []validation.Constraint{{Target: "parameters.Kind", Name: validation.Pattern, Rule: `^[-\w\._,\(\)]+$`, Chain: nil}}}}}}); err != nil { + errChan <- validation.NewErrorWithValidationError(err, "resources.GroupClient", "CreateOrUpdate") + close(errChan) + close(resultChan) + return resultChan, errChan + } + + go func() { + var err error + var result GenericResource + defer func() { + if err != nil { + errChan <- err + } + resultChan <- result + close(resultChan) + close(errChan) + }() + req, err := client.CreateOrUpdatePreparer(resourceGroupName, resourceProviderNamespace, parentResourcePath, resourceType, resourceName, parameters, cancel) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupClient", "CreateOrUpdate", nil, "Failure preparing request") + return + } + + resp, err := client.CreateOrUpdateSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "resources.GroupClient", "CreateOrUpdate", resp, "Failure sending request") + return + } + + result, err = client.CreateOrUpdateResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupClient", "CreateOrUpdate", resp, "Failure responding to request") + } + }() + return resultChan, errChan +} + +// CreateOrUpdatePreparer prepares the CreateOrUpdate request. +func (client GroupClient) CreateOrUpdatePreparer(resourceGroupName string, resourceProviderNamespace string, parentResourcePath string, resourceType string, resourceName string, parameters GenericResource, cancel <-chan struct{}) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "parentResourcePath": parentResourcePath, + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "resourceName": autorest.Encode("path", resourceName), + "resourceProviderNamespace": autorest.Encode("path", resourceProviderNamespace), + "resourceType": resourceType, + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsJSON(), + autorest.AsPut(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{parentResourcePath}/{resourceType}/{resourceName}", pathParameters), + autorest.WithJSON(parameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{Cancel: cancel}) +} + +// CreateOrUpdateSender sends the CreateOrUpdate request. The method will close the +// http.Response Body if it receives an error. +func (client GroupClient) CreateOrUpdateSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, + req, + azure.DoPollForAsynchronous(client.PollingDelay)) +} + +// CreateOrUpdateResponder handles the response to the CreateOrUpdate request. The method always +// closes the http.Response Body. +func (client GroupClient) CreateOrUpdateResponder(resp *http.Response) (result GenericResource, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusCreated, http.StatusOK, http.StatusAccepted), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// CreateOrUpdateByID create a resource by ID. This method may poll for completion. Polling can be canceled by passing +// the cancel channel argument. The channel will be used to cancel polling and any outstanding HTTP requests. +// +// resourceID is the fully qualified ID of the resource, including the resource name and resource type. Use the format, +// /subscriptions/{guid}/resourceGroups/{resource-group-name}/{resource-provider-namespace}/{resource-type}/{resource-name} +// parameters is create or update resource parameters. +func (client GroupClient) CreateOrUpdateByID(resourceID string, parameters GenericResource, cancel <-chan struct{}) (<-chan GenericResource, <-chan error) { + resultChan := make(chan GenericResource, 1) + errChan := make(chan error, 1) + if err := validation.Validate([]validation.Validation{ + {TargetValue: parameters, + Constraints: []validation.Constraint{{Target: "parameters.Kind", Name: validation.Null, Rule: false, + Chain: []validation.Constraint{{Target: "parameters.Kind", Name: validation.Pattern, Rule: `^[-\w\._,\(\)]+$`, Chain: nil}}}}}}); err != nil { + errChan <- validation.NewErrorWithValidationError(err, "resources.GroupClient", "CreateOrUpdateByID") + close(errChan) + close(resultChan) + return resultChan, errChan + } + + go func() { + var err error + var result GenericResource + defer func() { + if err != nil { + errChan <- err + } + resultChan <- result + close(resultChan) + close(errChan) + }() + req, err := client.CreateOrUpdateByIDPreparer(resourceID, parameters, cancel) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupClient", "CreateOrUpdateByID", nil, "Failure preparing request") + return + } + + resp, err := client.CreateOrUpdateByIDSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "resources.GroupClient", "CreateOrUpdateByID", resp, "Failure sending request") + return + } + + result, err = client.CreateOrUpdateByIDResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupClient", "CreateOrUpdateByID", resp, "Failure responding to request") + } + }() + return resultChan, errChan +} + +// CreateOrUpdateByIDPreparer prepares the CreateOrUpdateByID request. +func (client GroupClient) CreateOrUpdateByIDPreparer(resourceID string, parameters GenericResource, cancel <-chan struct{}) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "resourceId": resourceID, + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsJSON(), + autorest.AsPut(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/{resourceId}", pathParameters), + autorest.WithJSON(parameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{Cancel: cancel}) +} + +// CreateOrUpdateByIDSender sends the CreateOrUpdateByID request. The method will close the +// http.Response Body if it receives an error. +func (client GroupClient) CreateOrUpdateByIDSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, + req, + azure.DoPollForAsynchronous(client.PollingDelay)) +} + +// CreateOrUpdateByIDResponder handles the response to the CreateOrUpdateByID request. The method always +// closes the http.Response Body. +func (client GroupClient) CreateOrUpdateByIDResponder(resp *http.Response) (result GenericResource, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusCreated, http.StatusOK, http.StatusAccepted), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// Delete deletes a resource. This method may poll for completion. Polling can be canceled by passing the cancel +// channel argument. The channel will be used to cancel polling and any outstanding HTTP requests. +// +// resourceGroupName is the name of the resource group that contains the resource to delete. The name is case +// insensitive. resourceProviderNamespace is the namespace of the resource provider. parentResourcePath is the parent +// resource identity. resourceType is the resource type. resourceName is the name of the resource to delete. +func (client GroupClient) Delete(resourceGroupName string, resourceProviderNamespace string, parentResourcePath string, resourceType string, resourceName string, cancel <-chan struct{}) (<-chan autorest.Response, <-chan error) { + resultChan := make(chan autorest.Response, 1) + errChan := make(chan error, 1) + if err := validation.Validate([]validation.Validation{ + {TargetValue: resourceGroupName, + Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil}, + {Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}}); err != nil { + errChan <- validation.NewErrorWithValidationError(err, "resources.GroupClient", "Delete") + close(errChan) + close(resultChan) + return resultChan, errChan + } + + go func() { + var err error + var result autorest.Response + defer func() { + if err != nil { + errChan <- err + } + resultChan <- result + close(resultChan) + close(errChan) + }() + req, err := client.DeletePreparer(resourceGroupName, resourceProviderNamespace, parentResourcePath, resourceType, resourceName, cancel) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupClient", "Delete", nil, "Failure preparing request") + return + } + + resp, err := client.DeleteSender(req) + if err != nil { + result.Response = resp + err = autorest.NewErrorWithError(err, "resources.GroupClient", "Delete", resp, "Failure sending request") + return + } + + result, err = client.DeleteResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupClient", "Delete", resp, "Failure responding to request") + } + }() + return resultChan, errChan +} + +// DeletePreparer prepares the Delete request. +func (client GroupClient) DeletePreparer(resourceGroupName string, resourceProviderNamespace string, parentResourcePath string, resourceType string, resourceName string, cancel <-chan struct{}) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "parentResourcePath": parentResourcePath, + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "resourceName": autorest.Encode("path", resourceName), + "resourceProviderNamespace": autorest.Encode("path", resourceProviderNamespace), + "resourceType": resourceType, + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsDelete(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{parentResourcePath}/{resourceType}/{resourceName}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{Cancel: cancel}) +} + +// DeleteSender sends the Delete request. The method will close the +// http.Response Body if it receives an error. +func (client GroupClient) DeleteSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, + req, + azure.DoPollForAsynchronous(client.PollingDelay)) +} + +// DeleteResponder handles the response to the Delete request. The method always +// closes the http.Response Body. +func (client GroupClient) DeleteResponder(resp *http.Response) (result autorest.Response, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusNoContent, http.StatusAccepted), + autorest.ByClosing()) + result.Response = resp + return +} + +// DeleteByID deletes a resource by ID. This method may poll for completion. Polling can be canceled by passing the +// cancel channel argument. The channel will be used to cancel polling and any outstanding HTTP requests. +// +// resourceID is the fully qualified ID of the resource, including the resource name and resource type. Use the format, +// /subscriptions/{guid}/resourceGroups/{resource-group-name}/{resource-provider-namespace}/{resource-type}/{resource-name} +func (client GroupClient) DeleteByID(resourceID string, cancel <-chan struct{}) (<-chan autorest.Response, <-chan error) { + resultChan := make(chan autorest.Response, 1) + errChan := make(chan error, 1) + go func() { + var err error + var result autorest.Response + defer func() { + if err != nil { + errChan <- err + } + resultChan <- result + close(resultChan) + close(errChan) + }() + req, err := client.DeleteByIDPreparer(resourceID, cancel) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupClient", "DeleteByID", nil, "Failure preparing request") + return + } + + resp, err := client.DeleteByIDSender(req) + if err != nil { + result.Response = resp + err = autorest.NewErrorWithError(err, "resources.GroupClient", "DeleteByID", resp, "Failure sending request") + return + } + + result, err = client.DeleteByIDResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupClient", "DeleteByID", resp, "Failure responding to request") + } + }() + return resultChan, errChan +} + +// DeleteByIDPreparer prepares the DeleteByID request. +func (client GroupClient) DeleteByIDPreparer(resourceID string, cancel <-chan struct{}) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "resourceId": resourceID, + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsDelete(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/{resourceId}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{Cancel: cancel}) +} + +// DeleteByIDSender sends the DeleteByID request. The method will close the +// http.Response Body if it receives an error. +func (client GroupClient) DeleteByIDSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, + req, + azure.DoPollForAsynchronous(client.PollingDelay)) +} + +// DeleteByIDResponder handles the response to the DeleteByID request. The method always +// closes the http.Response Body. +func (client GroupClient) DeleteByIDResponder(resp *http.Response) (result autorest.Response, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusNoContent, http.StatusAccepted), + autorest.ByClosing()) + result.Response = resp + return +} + +// Get gets a resource. +// +// resourceGroupName is the name of the resource group containing the resource to get. The name is case insensitive. +// resourceProviderNamespace is the namespace of the resource provider. parentResourcePath is the parent resource +// identity. resourceType is the resource type of the resource. resourceName is the name of the resource to get. +func (client GroupClient) Get(resourceGroupName string, resourceProviderNamespace string, parentResourcePath string, resourceType string, resourceName string) (result GenericResource, err error) { + if err := validation.Validate([]validation.Validation{ + {TargetValue: resourceGroupName, + Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil}, + {Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}}); err != nil { + return result, validation.NewErrorWithValidationError(err, "resources.GroupClient", "Get") + } + + req, err := client.GetPreparer(resourceGroupName, resourceProviderNamespace, parentResourcePath, resourceType, resourceName) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupClient", "Get", nil, "Failure preparing request") + return + } + + resp, err := client.GetSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "resources.GroupClient", "Get", resp, "Failure sending request") + return + } + + result, err = client.GetResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupClient", "Get", resp, "Failure responding to request") + } + + return +} + +// GetPreparer prepares the Get request. +func (client GroupClient) GetPreparer(resourceGroupName string, resourceProviderNamespace string, parentResourcePath string, resourceType string, resourceName string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "parentResourcePath": parentResourcePath, + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "resourceName": autorest.Encode("path", resourceName), + "resourceProviderNamespace": autorest.Encode("path", resourceProviderNamespace), + "resourceType": resourceType, + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{parentResourcePath}/{resourceType}/{resourceName}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// GetSender sends the Get request. The method will close the +// http.Response Body if it receives an error. +func (client GroupClient) GetSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// GetResponder handles the response to the Get request. The method always +// closes the http.Response Body. +func (client GroupClient) GetResponder(resp *http.Response) (result GenericResource, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// GetByID gets a resource by ID. +// +// resourceID is the fully qualified ID of the resource, including the resource name and resource type. Use the format, +// /subscriptions/{guid}/resourceGroups/{resource-group-name}/{resource-provider-namespace}/{resource-type}/{resource-name} +func (client GroupClient) GetByID(resourceID string) (result GenericResource, err error) { + req, err := client.GetByIDPreparer(resourceID) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupClient", "GetByID", nil, "Failure preparing request") + return + } + + resp, err := client.GetByIDSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "resources.GroupClient", "GetByID", resp, "Failure sending request") + return + } + + result, err = client.GetByIDResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupClient", "GetByID", resp, "Failure responding to request") + } + + return +} + +// GetByIDPreparer prepares the GetByID request. +func (client GroupClient) GetByIDPreparer(resourceID string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "resourceId": resourceID, + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/{resourceId}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// GetByIDSender sends the GetByID request. The method will close the +// http.Response Body if it receives an error. +func (client GroupClient) GetByIDSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// GetByIDResponder handles the response to the GetByID request. The method always +// closes the http.Response Body. +func (client GroupClient) GetByIDResponder(resp *http.Response) (result GenericResource, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// List get all the resources in a subscription. +// +// filter is the filter to apply on the operation. expand is the $expand query parameter. top is the number of results +// to return. If null is passed, returns all resource groups. +func (client GroupClient) List(filter string, expand string, top *int32) (result ListResult, err error) { + req, err := client.ListPreparer(filter, expand, top) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupClient", "List", nil, "Failure preparing request") + return + } + + resp, err := client.ListSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "resources.GroupClient", "List", resp, "Failure sending request") + return + } + + result, err = client.ListResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupClient", "List", resp, "Failure responding to request") + } + + return +} + +// ListPreparer prepares the List request. +func (client GroupClient) ListPreparer(filter string, expand string, top *int32) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + if len(filter) > 0 { + queryParameters["$filter"] = autorest.Encode("query", filter) + } + if len(expand) > 0 { + queryParameters["$expand"] = autorest.Encode("query", expand) + } + if top != nil { + queryParameters["$top"] = autorest.Encode("query", *top) + } + + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resources", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// ListSender sends the List request. The method will close the +// http.Response Body if it receives an error. +func (client GroupClient) ListSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// ListResponder handles the response to the List request. The method always +// closes the http.Response Body. +func (client GroupClient) ListResponder(resp *http.Response) (result ListResult, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// ListNextResults retrieves the next set of results, if any. +func (client GroupClient) ListNextResults(lastResults ListResult) (result ListResult, err error) { + req, err := lastResults.ListResultPreparer() + if err != nil { + return result, autorest.NewErrorWithError(err, "resources.GroupClient", "List", nil, "Failure preparing next results request") + } + if req == nil { + return + } + + resp, err := client.ListSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + return result, autorest.NewErrorWithError(err, "resources.GroupClient", "List", resp, "Failure sending next results request") + } + + result, err = client.ListResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupClient", "List", resp, "Failure responding to next results request") + } + + return +} + +// ListComplete gets all elements from the list without paging. +func (client GroupClient) ListComplete(filter string, expand string, top *int32, cancel <-chan struct{}) (<-chan GenericResource, <-chan error) { + resultChan := make(chan GenericResource) + errChan := make(chan error, 1) + go func() { + defer func() { + close(resultChan) + close(errChan) + }() + list, err := client.List(filter, expand, top) + if err != nil { + errChan <- err + return + } + if list.Value != nil { + for _, item := range *list.Value { + select { + case <-cancel: + return + case resultChan <- item: + // Intentionally left blank + } + } + } + for list.NextLink != nil { + list, err = client.ListNextResults(list) + if err != nil { + errChan <- err + return + } + if list.Value != nil { + for _, item := range *list.Value { + select { + case <-cancel: + return + case resultChan <- item: + // Intentionally left blank + } + } + } + } + }() + return resultChan, errChan +} + +// ListByResourceGroup get all the resources for a resource group. +// +// resourceGroupName is the resource group with the resources to get. filter is the filter to apply on the operation. +// expand is the $expand query parameter top is the number of results to return. If null is passed, returns all +// resources. +func (client GroupClient) ListByResourceGroup(resourceGroupName string, filter string, expand string, top *int32) (result ListResult, err error) { + if err := validation.Validate([]validation.Validation{ + {TargetValue: resourceGroupName, + Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil}, + {Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}}); err != nil { + return result, validation.NewErrorWithValidationError(err, "resources.GroupClient", "ListByResourceGroup") + } + + req, err := client.ListByResourceGroupPreparer(resourceGroupName, filter, expand, top) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupClient", "ListByResourceGroup", nil, "Failure preparing request") + return + } + + resp, err := client.ListByResourceGroupSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "resources.GroupClient", "ListByResourceGroup", resp, "Failure sending request") + return + } + + result, err = client.ListByResourceGroupResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupClient", "ListByResourceGroup", resp, "Failure responding to request") + } + + return +} + +// ListByResourceGroupPreparer prepares the ListByResourceGroup request. +func (client GroupClient) ListByResourceGroupPreparer(resourceGroupName string, filter string, expand string, top *int32) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + if len(filter) > 0 { + queryParameters["$filter"] = autorest.Encode("query", filter) + } + if len(expand) > 0 { + queryParameters["$expand"] = autorest.Encode("query", expand) + } + if top != nil { + queryParameters["$top"] = autorest.Encode("query", *top) + } + + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/resources", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// ListByResourceGroupSender sends the ListByResourceGroup request. The method will close the +// http.Response Body if it receives an error. +func (client GroupClient) ListByResourceGroupSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// ListByResourceGroupResponder handles the response to the ListByResourceGroup request. The method always +// closes the http.Response Body. +func (client GroupClient) ListByResourceGroupResponder(resp *http.Response) (result ListResult, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// ListByResourceGroupNextResults retrieves the next set of results, if any. +func (client GroupClient) ListByResourceGroupNextResults(lastResults ListResult) (result ListResult, err error) { + req, err := lastResults.ListResultPreparer() + if err != nil { + return result, autorest.NewErrorWithError(err, "resources.GroupClient", "ListByResourceGroup", nil, "Failure preparing next results request") + } + if req == nil { + return + } + + resp, err := client.ListByResourceGroupSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + return result, autorest.NewErrorWithError(err, "resources.GroupClient", "ListByResourceGroup", resp, "Failure sending next results request") + } + + result, err = client.ListByResourceGroupResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupClient", "ListByResourceGroup", resp, "Failure responding to next results request") + } + + return +} + +// ListByResourceGroupComplete gets all elements from the list without paging. +func (client GroupClient) ListByResourceGroupComplete(resourceGroupName string, filter string, expand string, top *int32, cancel <-chan struct{}) (<-chan GenericResource, <-chan error) { + resultChan := make(chan GenericResource) + errChan := make(chan error, 1) + go func() { + defer func() { + close(resultChan) + close(errChan) + }() + list, err := client.ListByResourceGroup(resourceGroupName, filter, expand, top) + if err != nil { + errChan <- err + return + } + if list.Value != nil { + for _, item := range *list.Value { + select { + case <-cancel: + return + case resultChan <- item: + // Intentionally left blank + } + } + } + for list.NextLink != nil { + list, err = client.ListByResourceGroupNextResults(list) + if err != nil { + errChan <- err + return + } + if list.Value != nil { + for _, item := range *list.Value { + select { + case <-cancel: + return + case resultChan <- item: + // Intentionally left blank + } + } + } + } + }() + return resultChan, errChan +} + +// MoveResources the resources to move must be in the same source resource group. The target resource group may be in a +// different subscription. When moving resources, both the source group and the target group are locked for the +// duration of the operation. Write and delete operations are blocked on the groups until the move completes. This +// method may poll for completion. Polling can be canceled by passing the cancel channel argument. The channel will be +// used to cancel polling and any outstanding HTTP requests. +// +// sourceResourceGroupName is the name of the resource group containing the resources to move. parameters is parameters +// for moving resources. +func (client GroupClient) MoveResources(sourceResourceGroupName string, parameters MoveInfo, cancel <-chan struct{}) (<-chan autorest.Response, <-chan error) { + resultChan := make(chan autorest.Response, 1) + errChan := make(chan error, 1) + if err := validation.Validate([]validation.Validation{ + {TargetValue: sourceResourceGroupName, + Constraints: []validation.Constraint{{Target: "sourceResourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil}, + {Target: "sourceResourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "sourceResourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}}); err != nil { + errChan <- validation.NewErrorWithValidationError(err, "resources.GroupClient", "MoveResources") + close(errChan) + close(resultChan) + return resultChan, errChan + } + + go func() { + var err error + var result autorest.Response + defer func() { + if err != nil { + errChan <- err + } + resultChan <- result + close(resultChan) + close(errChan) + }() + req, err := client.MoveResourcesPreparer(sourceResourceGroupName, parameters, cancel) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupClient", "MoveResources", nil, "Failure preparing request") + return + } + + resp, err := client.MoveResourcesSender(req) + if err != nil { + result.Response = resp + err = autorest.NewErrorWithError(err, "resources.GroupClient", "MoveResources", resp, "Failure sending request") + return + } + + result, err = client.MoveResourcesResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupClient", "MoveResources", resp, "Failure responding to request") + } + }() + return resultChan, errChan +} + +// MoveResourcesPreparer prepares the MoveResources request. +func (client GroupClient) MoveResourcesPreparer(sourceResourceGroupName string, parameters MoveInfo, cancel <-chan struct{}) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "sourceResourceGroupName": autorest.Encode("path", sourceResourceGroupName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsJSON(), + autorest.AsPost(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{sourceResourceGroupName}/moveResources", pathParameters), + autorest.WithJSON(parameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{Cancel: cancel}) +} + +// MoveResourcesSender sends the MoveResources request. The method will close the +// http.Response Body if it receives an error. +func (client GroupClient) MoveResourcesSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, + req, + azure.DoPollForAsynchronous(client.PollingDelay)) +} + +// MoveResourcesResponder handles the response to the MoveResources request. The method always +// closes the http.Response Body. +func (client GroupClient) MoveResourcesResponder(resp *http.Response) (result autorest.Response, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted, http.StatusNoContent), + autorest.ByClosing()) + result.Response = resp + return +} + +// ValidateMoveResources this operation checks whether the specified resources can be moved to the target. The +// resources to move must be in the same source resource group. The target resource group may be in a different +// subscription. If validation succeeds, it returns HTTP response code 204 (no content). If validation fails, it +// returns HTTP response code 409 (Conflict) with an error message. Retrieve the URL in the Location header value to +// check the result of the long-running operation. This method may poll for completion. Polling can be canceled by +// passing the cancel channel argument. The channel will be used to cancel polling and any outstanding HTTP requests. +// +// sourceResourceGroupName is the name of the resource group containing the resources to validate for move. parameters +// is parameters for moving resources. +func (client GroupClient) ValidateMoveResources(sourceResourceGroupName string, parameters MoveInfo, cancel <-chan struct{}) (<-chan autorest.Response, <-chan error) { + resultChan := make(chan autorest.Response, 1) + errChan := make(chan error, 1) + if err := validation.Validate([]validation.Validation{ + {TargetValue: sourceResourceGroupName, + Constraints: []validation.Constraint{{Target: "sourceResourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil}, + {Target: "sourceResourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "sourceResourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}}); err != nil { + errChan <- validation.NewErrorWithValidationError(err, "resources.GroupClient", "ValidateMoveResources") + close(errChan) + close(resultChan) + return resultChan, errChan + } + + go func() { + var err error + var result autorest.Response + defer func() { + if err != nil { + errChan <- err + } + resultChan <- result + close(resultChan) + close(errChan) + }() + req, err := client.ValidateMoveResourcesPreparer(sourceResourceGroupName, parameters, cancel) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupClient", "ValidateMoveResources", nil, "Failure preparing request") + return + } + + resp, err := client.ValidateMoveResourcesSender(req) + if err != nil { + result.Response = resp + err = autorest.NewErrorWithError(err, "resources.GroupClient", "ValidateMoveResources", resp, "Failure sending request") + return + } + + result, err = client.ValidateMoveResourcesResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupClient", "ValidateMoveResources", resp, "Failure responding to request") + } + }() + return resultChan, errChan +} + +// ValidateMoveResourcesPreparer prepares the ValidateMoveResources request. +func (client GroupClient) ValidateMoveResourcesPreparer(sourceResourceGroupName string, parameters MoveInfo, cancel <-chan struct{}) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "sourceResourceGroupName": autorest.Encode("path", sourceResourceGroupName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsJSON(), + autorest.AsPost(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{sourceResourceGroupName}/validateMoveResources", pathParameters), + autorest.WithJSON(parameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{Cancel: cancel}) +} + +// ValidateMoveResourcesSender sends the ValidateMoveResources request. The method will close the +// http.Response Body if it receives an error. +func (client GroupClient) ValidateMoveResourcesSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, + req, + azure.DoPollForAsynchronous(client.PollingDelay)) +} + +// ValidateMoveResourcesResponder handles the response to the ValidateMoveResources request. The method always +// closes the http.Response Body. +func (client GroupClient) ValidateMoveResourcesResponder(resp *http.Response) (result autorest.Response, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted, http.StatusNoContent, http.StatusConflict), + autorest.ByClosing()) + result.Response = resp + return +} diff --git a/cluster-autoscaler/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/resources/groups.go b/cluster-autoscaler/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/resources/groups.go new file mode 100755 index 000000000000..3eae2eab75c2 --- /dev/null +++ b/cluster-autoscaler/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/resources/groups.go @@ -0,0 +1,642 @@ +package resources + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/azure" + "github.com/Azure/go-autorest/autorest/validation" + "net/http" +) + +// GroupsClient is the provides operations for working with resources and resource groups. +type GroupsClient struct { + ManagementClient +} + +// NewGroupsClient creates an instance of the GroupsClient client. +func NewGroupsClient(subscriptionID string) GroupsClient { + return NewGroupsClientWithBaseURI(DefaultBaseURI, subscriptionID) +} + +// NewGroupsClientWithBaseURI creates an instance of the GroupsClient client. +func NewGroupsClientWithBaseURI(baseURI string, subscriptionID string) GroupsClient { + return GroupsClient{NewWithBaseURI(baseURI, subscriptionID)} +} + +// CheckExistence checks whether a resource group exists. +// +// resourceGroupName is the name of the resource group to check. The name is case insensitive. +func (client GroupsClient) CheckExistence(resourceGroupName string) (result autorest.Response, err error) { + if err := validation.Validate([]validation.Validation{ + {TargetValue: resourceGroupName, + Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil}, + {Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}}); err != nil { + return result, validation.NewErrorWithValidationError(err, "resources.GroupsClient", "CheckExistence") + } + + req, err := client.CheckExistencePreparer(resourceGroupName) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupsClient", "CheckExistence", nil, "Failure preparing request") + return + } + + resp, err := client.CheckExistenceSender(req) + if err != nil { + result.Response = resp + err = autorest.NewErrorWithError(err, "resources.GroupsClient", "CheckExistence", resp, "Failure sending request") + return + } + + result, err = client.CheckExistenceResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupsClient", "CheckExistence", resp, "Failure responding to request") + } + + return +} + +// CheckExistencePreparer prepares the CheckExistence request. +func (client GroupsClient) CheckExistencePreparer(resourceGroupName string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsHead(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// CheckExistenceSender sends the CheckExistence request. The method will close the +// http.Response Body if it receives an error. +func (client GroupsClient) CheckExistenceSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// CheckExistenceResponder handles the response to the CheckExistence request. The method always +// closes the http.Response Body. +func (client GroupsClient) CheckExistenceResponder(resp *http.Response) (result autorest.Response, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusNoContent, http.StatusNotFound), + autorest.ByClosing()) + result.Response = resp + return +} + +// CreateOrUpdate creates or updates a resource group. +// +// resourceGroupName is the name of the resource group to create or update. parameters is parameters supplied to the +// create or update a resource group. +func (client GroupsClient) CreateOrUpdate(resourceGroupName string, parameters Group) (result Group, err error) { + if err := validation.Validate([]validation.Validation{ + {TargetValue: resourceGroupName, + Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil}, + {Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}, + {TargetValue: parameters, + Constraints: []validation.Constraint{{Target: "parameters.Location", Name: validation.Null, Rule: true, Chain: nil}}}}); err != nil { + return result, validation.NewErrorWithValidationError(err, "resources.GroupsClient", "CreateOrUpdate") + } + + req, err := client.CreateOrUpdatePreparer(resourceGroupName, parameters) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupsClient", "CreateOrUpdate", nil, "Failure preparing request") + return + } + + resp, err := client.CreateOrUpdateSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "resources.GroupsClient", "CreateOrUpdate", resp, "Failure sending request") + return + } + + result, err = client.CreateOrUpdateResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupsClient", "CreateOrUpdate", resp, "Failure responding to request") + } + + return +} + +// CreateOrUpdatePreparer prepares the CreateOrUpdate request. +func (client GroupsClient) CreateOrUpdatePreparer(resourceGroupName string, parameters Group) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsJSON(), + autorest.AsPut(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}", pathParameters), + autorest.WithJSON(parameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// CreateOrUpdateSender sends the CreateOrUpdate request. The method will close the +// http.Response Body if it receives an error. +func (client GroupsClient) CreateOrUpdateSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// CreateOrUpdateResponder handles the response to the CreateOrUpdate request. The method always +// closes the http.Response Body. +func (client GroupsClient) CreateOrUpdateResponder(resp *http.Response) (result Group, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusCreated, http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// Delete when you delete a resource group, all of its resources are also deleted. Deleting a resource group deletes +// all of its template deployments and currently stored operations. This method may poll for completion. Polling can be +// canceled by passing the cancel channel argument. The channel will be used to cancel polling and any outstanding HTTP +// requests. +// +// resourceGroupName is the name of the resource group to delete. The name is case insensitive. +func (client GroupsClient) Delete(resourceGroupName string, cancel <-chan struct{}) (<-chan autorest.Response, <-chan error) { + resultChan := make(chan autorest.Response, 1) + errChan := make(chan error, 1) + if err := validation.Validate([]validation.Validation{ + {TargetValue: resourceGroupName, + Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil}, + {Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}}); err != nil { + errChan <- validation.NewErrorWithValidationError(err, "resources.GroupsClient", "Delete") + close(errChan) + close(resultChan) + return resultChan, errChan + } + + go func() { + var err error + var result autorest.Response + defer func() { + if err != nil { + errChan <- err + } + resultChan <- result + close(resultChan) + close(errChan) + }() + req, err := client.DeletePreparer(resourceGroupName, cancel) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupsClient", "Delete", nil, "Failure preparing request") + return + } + + resp, err := client.DeleteSender(req) + if err != nil { + result.Response = resp + err = autorest.NewErrorWithError(err, "resources.GroupsClient", "Delete", resp, "Failure sending request") + return + } + + result, err = client.DeleteResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupsClient", "Delete", resp, "Failure responding to request") + } + }() + return resultChan, errChan +} + +// DeletePreparer prepares the Delete request. +func (client GroupsClient) DeletePreparer(resourceGroupName string, cancel <-chan struct{}) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsDelete(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{Cancel: cancel}) +} + +// DeleteSender sends the Delete request. The method will close the +// http.Response Body if it receives an error. +func (client GroupsClient) DeleteSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, + req, + azure.DoPollForAsynchronous(client.PollingDelay)) +} + +// DeleteResponder handles the response to the Delete request. The method always +// closes the http.Response Body. +func (client GroupsClient) DeleteResponder(resp *http.Response) (result autorest.Response, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusAccepted, http.StatusOK), + autorest.ByClosing()) + result.Response = resp + return +} + +// ExportTemplate captures the specified resource group as a template. +// +// resourceGroupName is the name of the resource group to export as a template. parameters is parameters for exporting +// the template. +func (client GroupsClient) ExportTemplate(resourceGroupName string, parameters ExportTemplateRequest) (result GroupExportResult, err error) { + if err := validation.Validate([]validation.Validation{ + {TargetValue: resourceGroupName, + Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil}, + {Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}}); err != nil { + return result, validation.NewErrorWithValidationError(err, "resources.GroupsClient", "ExportTemplate") + } + + req, err := client.ExportTemplatePreparer(resourceGroupName, parameters) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupsClient", "ExportTemplate", nil, "Failure preparing request") + return + } + + resp, err := client.ExportTemplateSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "resources.GroupsClient", "ExportTemplate", resp, "Failure sending request") + return + } + + result, err = client.ExportTemplateResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupsClient", "ExportTemplate", resp, "Failure responding to request") + } + + return +} + +// ExportTemplatePreparer prepares the ExportTemplate request. +func (client GroupsClient) ExportTemplatePreparer(resourceGroupName string, parameters ExportTemplateRequest) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsJSON(), + autorest.AsPost(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/exportTemplate", pathParameters), + autorest.WithJSON(parameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// ExportTemplateSender sends the ExportTemplate request. The method will close the +// http.Response Body if it receives an error. +func (client GroupsClient) ExportTemplateSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// ExportTemplateResponder handles the response to the ExportTemplate request. The method always +// closes the http.Response Body. +func (client GroupsClient) ExportTemplateResponder(resp *http.Response) (result GroupExportResult, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// Get gets a resource group. +// +// resourceGroupName is the name of the resource group to get. The name is case insensitive. +func (client GroupsClient) Get(resourceGroupName string) (result Group, err error) { + if err := validation.Validate([]validation.Validation{ + {TargetValue: resourceGroupName, + Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil}, + {Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}}); err != nil { + return result, validation.NewErrorWithValidationError(err, "resources.GroupsClient", "Get") + } + + req, err := client.GetPreparer(resourceGroupName) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupsClient", "Get", nil, "Failure preparing request") + return + } + + resp, err := client.GetSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "resources.GroupsClient", "Get", resp, "Failure sending request") + return + } + + result, err = client.GetResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupsClient", "Get", resp, "Failure responding to request") + } + + return +} + +// GetPreparer prepares the Get request. +func (client GroupsClient) GetPreparer(resourceGroupName string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// GetSender sends the Get request. The method will close the +// http.Response Body if it receives an error. +func (client GroupsClient) GetSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// GetResponder handles the response to the Get request. The method always +// closes the http.Response Body. +func (client GroupsClient) GetResponder(resp *http.Response) (result Group, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// List gets all the resource groups for a subscription. +// +// filter is the filter to apply on the operation. top is the number of results to return. If null is passed, returns +// all resource groups. +func (client GroupsClient) List(filter string, top *int32) (result GroupListResult, err error) { + req, err := client.ListPreparer(filter, top) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupsClient", "List", nil, "Failure preparing request") + return + } + + resp, err := client.ListSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "resources.GroupsClient", "List", resp, "Failure sending request") + return + } + + result, err = client.ListResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupsClient", "List", resp, "Failure responding to request") + } + + return +} + +// ListPreparer prepares the List request. +func (client GroupsClient) ListPreparer(filter string, top *int32) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + if len(filter) > 0 { + queryParameters["$filter"] = autorest.Encode("query", filter) + } + if top != nil { + queryParameters["$top"] = autorest.Encode("query", *top) + } + + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourcegroups", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// ListSender sends the List request. The method will close the +// http.Response Body if it receives an error. +func (client GroupsClient) ListSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// ListResponder handles the response to the List request. The method always +// closes the http.Response Body. +func (client GroupsClient) ListResponder(resp *http.Response) (result GroupListResult, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// ListNextResults retrieves the next set of results, if any. +func (client GroupsClient) ListNextResults(lastResults GroupListResult) (result GroupListResult, err error) { + req, err := lastResults.GroupListResultPreparer() + if err != nil { + return result, autorest.NewErrorWithError(err, "resources.GroupsClient", "List", nil, "Failure preparing next results request") + } + if req == nil { + return + } + + resp, err := client.ListSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + return result, autorest.NewErrorWithError(err, "resources.GroupsClient", "List", resp, "Failure sending next results request") + } + + result, err = client.ListResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupsClient", "List", resp, "Failure responding to next results request") + } + + return +} + +// ListComplete gets all elements from the list without paging. +func (client GroupsClient) ListComplete(filter string, top *int32, cancel <-chan struct{}) (<-chan Group, <-chan error) { + resultChan := make(chan Group) + errChan := make(chan error, 1) + go func() { + defer func() { + close(resultChan) + close(errChan) + }() + list, err := client.List(filter, top) + if err != nil { + errChan <- err + return + } + if list.Value != nil { + for _, item := range *list.Value { + select { + case <-cancel: + return + case resultChan <- item: + // Intentionally left blank + } + } + } + for list.NextLink != nil { + list, err = client.ListNextResults(list) + if err != nil { + errChan <- err + return + } + if list.Value != nil { + for _, item := range *list.Value { + select { + case <-cancel: + return + case resultChan <- item: + // Intentionally left blank + } + } + } + } + }() + return resultChan, errChan +} + +// Update resource groups can be updated through a simple PATCH operation to a group address. The format of the request +// is the same as that for creating a resource group. If a field is unspecified, the current value is retained. +// +// resourceGroupName is the name of the resource group to update. The name is case insensitive. parameters is +// parameters supplied to update a resource group. +func (client GroupsClient) Update(resourceGroupName string, parameters GroupPatchable) (result Group, err error) { + if err := validation.Validate([]validation.Validation{ + {TargetValue: resourceGroupName, + Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil}, + {Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}}); err != nil { + return result, validation.NewErrorWithValidationError(err, "resources.GroupsClient", "Update") + } + + req, err := client.UpdatePreparer(resourceGroupName, parameters) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupsClient", "Update", nil, "Failure preparing request") + return + } + + resp, err := client.UpdateSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "resources.GroupsClient", "Update", resp, "Failure sending request") + return + } + + result, err = client.UpdateResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.GroupsClient", "Update", resp, "Failure responding to request") + } + + return +} + +// UpdatePreparer prepares the Update request. +func (client GroupsClient) UpdatePreparer(resourceGroupName string, parameters GroupPatchable) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsJSON(), + autorest.AsPatch(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}", pathParameters), + autorest.WithJSON(parameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// UpdateSender sends the Update request. The method will close the +// http.Response Body if it receives an error. +func (client GroupsClient) UpdateSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// UpdateResponder handles the response to the Update request. The method always +// closes the http.Response Body. +func (client GroupsClient) UpdateResponder(resp *http.Response) (result Group, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} diff --git a/cluster-autoscaler/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/resources/models.go b/cluster-autoscaler/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/resources/models.go new file mode 100755 index 000000000000..70bd4c1b3980 --- /dev/null +++ b/cluster-autoscaler/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/resources/models.go @@ -0,0 +1,459 @@ +package resources + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/date" + "github.com/Azure/go-autorest/autorest/to" + "net/http" +) + +// DeploymentMode enumerates the values for deployment mode. +type DeploymentMode string + +const ( + // Complete specifies the complete state for deployment mode. + Complete DeploymentMode = "Complete" + // Incremental specifies the incremental state for deployment mode. + Incremental DeploymentMode = "Incremental" +) + +// ResourceIdentityType enumerates the values for resource identity type. +type ResourceIdentityType string + +const ( + // SystemAssigned specifies the system assigned state for resource identity type. + SystemAssigned ResourceIdentityType = "SystemAssigned" +) + +// AliasPathType is the type of the paths for alias. +type AliasPathType struct { + Path *string `json:"path,omitempty"` + APIVersions *[]string `json:"apiVersions,omitempty"` +} + +// AliasType is the alias type. +type AliasType struct { + Name *string `json:"name,omitempty"` + Paths *[]AliasPathType `json:"paths,omitempty"` +} + +// BasicDependency is deployment dependency information. +type BasicDependency struct { + ID *string `json:"id,omitempty"` + ResourceType *string `json:"resourceType,omitempty"` + ResourceName *string `json:"resourceName,omitempty"` +} + +// DebugSetting is +type DebugSetting struct { + DetailLevel *string `json:"detailLevel,omitempty"` +} + +// Dependency is deployment dependency information. +type Dependency struct { + DependsOn *[]BasicDependency `json:"dependsOn,omitempty"` + ID *string `json:"id,omitempty"` + ResourceType *string `json:"resourceType,omitempty"` + ResourceName *string `json:"resourceName,omitempty"` +} + +// Deployment is deployment operation parameters. +type Deployment struct { + Properties *DeploymentProperties `json:"properties,omitempty"` +} + +// DeploymentExportResult is the deployment export result. +type DeploymentExportResult struct { + autorest.Response `json:"-"` + Template *map[string]interface{} `json:"template,omitempty"` +} + +// DeploymentExtended is deployment information. +type DeploymentExtended struct { + autorest.Response `json:"-"` + ID *string `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + Properties *DeploymentPropertiesExtended `json:"properties,omitempty"` +} + +// DeploymentExtendedFilter is deployment filter. +type DeploymentExtendedFilter struct { + ProvisioningState *string `json:"provisioningState,omitempty"` +} + +// DeploymentListResult is list of deployments. +type DeploymentListResult struct { + autorest.Response `json:"-"` + Value *[]DeploymentExtended `json:"value,omitempty"` + NextLink *string `json:"nextLink,omitempty"` +} + +// DeploymentListResultPreparer prepares a request to retrieve the next set of results. It returns +// nil if no more results exist. +func (client DeploymentListResult) DeploymentListResultPreparer() (*http.Request, error) { + if client.NextLink == nil || len(to.String(client.NextLink)) <= 0 { + return nil, nil + } + return autorest.Prepare(&http.Request{}, + autorest.AsJSON(), + autorest.AsGet(), + autorest.WithBaseURL(to.String(client.NextLink))) +} + +// DeploymentOperation is deployment operation information. +type DeploymentOperation struct { + autorest.Response `json:"-"` + ID *string `json:"id,omitempty"` + OperationID *string `json:"operationId,omitempty"` + Properties *DeploymentOperationProperties `json:"properties,omitempty"` +} + +// DeploymentOperationProperties is deployment operation properties. +type DeploymentOperationProperties struct { + ProvisioningState *string `json:"provisioningState,omitempty"` + Timestamp *date.Time `json:"timestamp,omitempty"` + ServiceRequestID *string `json:"serviceRequestId,omitempty"` + StatusCode *string `json:"statusCode,omitempty"` + StatusMessage *map[string]interface{} `json:"statusMessage,omitempty"` + TargetResource *TargetResource `json:"targetResource,omitempty"` + Request *HTTPMessage `json:"request,omitempty"` + Response *HTTPMessage `json:"response,omitempty"` +} + +// DeploymentOperationsListResult is list of deployment operations. +type DeploymentOperationsListResult struct { + autorest.Response `json:"-"` + Value *[]DeploymentOperation `json:"value,omitempty"` + NextLink *string `json:"nextLink,omitempty"` +} + +// DeploymentOperationsListResultPreparer prepares a request to retrieve the next set of results. It returns +// nil if no more results exist. +func (client DeploymentOperationsListResult) DeploymentOperationsListResultPreparer() (*http.Request, error) { + if client.NextLink == nil || len(to.String(client.NextLink)) <= 0 { + return nil, nil + } + return autorest.Prepare(&http.Request{}, + autorest.AsJSON(), + autorest.AsGet(), + autorest.WithBaseURL(to.String(client.NextLink))) +} + +// DeploymentProperties is deployment properties. +type DeploymentProperties struct { + Template *map[string]interface{} `json:"template,omitempty"` + TemplateLink *TemplateLink `json:"templateLink,omitempty"` + Parameters *map[string]interface{} `json:"parameters,omitempty"` + ParametersLink *ParametersLink `json:"parametersLink,omitempty"` + Mode DeploymentMode `json:"mode,omitempty"` + DebugSetting *DebugSetting `json:"debugSetting,omitempty"` +} + +// DeploymentPropertiesExtended is deployment properties with additional details. +type DeploymentPropertiesExtended struct { + ProvisioningState *string `json:"provisioningState,omitempty"` + CorrelationID *string `json:"correlationId,omitempty"` + Timestamp *date.Time `json:"timestamp,omitempty"` + Outputs *map[string]interface{} `json:"outputs,omitempty"` + Providers *[]Provider `json:"providers,omitempty"` + Dependencies *[]Dependency `json:"dependencies,omitempty"` + Template *map[string]interface{} `json:"template,omitempty"` + TemplateLink *TemplateLink `json:"templateLink,omitempty"` + Parameters *map[string]interface{} `json:"parameters,omitempty"` + ParametersLink *ParametersLink `json:"parametersLink,omitempty"` + Mode DeploymentMode `json:"mode,omitempty"` + DebugSetting *DebugSetting `json:"debugSetting,omitempty"` +} + +// DeploymentValidateResult is information from validate template deployment response. +type DeploymentValidateResult struct { + autorest.Response `json:"-"` + Error *ManagementErrorWithDetails `json:"error,omitempty"` + Properties *DeploymentPropertiesExtended `json:"properties,omitempty"` +} + +// ExportTemplateRequest is export resource group template request parameters. +type ExportTemplateRequest struct { + ResourcesProperty *[]string `json:"resources,omitempty"` + Options *string `json:"options,omitempty"` +} + +// GenericResource is resource information. +type GenericResource struct { + autorest.Response `json:"-"` + ID *string `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + Type *string `json:"type,omitempty"` + Location *string `json:"location,omitempty"` + Tags *map[string]*string `json:"tags,omitempty"` + Plan *Plan `json:"plan,omitempty"` + Properties *map[string]interface{} `json:"properties,omitempty"` + Kind *string `json:"kind,omitempty"` + ManagedBy *string `json:"managedBy,omitempty"` + Sku *Sku `json:"sku,omitempty"` + Identity *Identity `json:"identity,omitempty"` +} + +// GenericResourceFilter is resource filter. +type GenericResourceFilter struct { + ResourceType *string `json:"resourceType,omitempty"` + Tagname *string `json:"tagname,omitempty"` + Tagvalue *string `json:"tagvalue,omitempty"` +} + +// Group is resource group information. +type Group struct { + autorest.Response `json:"-"` + ID *string `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + Properties *GroupProperties `json:"properties,omitempty"` + Location *string `json:"location,omitempty"` + ManagedBy *string `json:"managedBy,omitempty"` + Tags *map[string]*string `json:"tags,omitempty"` +} + +// GroupExportResult is resource group export result. +type GroupExportResult struct { + autorest.Response `json:"-"` + Template *map[string]interface{} `json:"template,omitempty"` + Error *ManagementErrorWithDetails `json:"error,omitempty"` +} + +// GroupFilter is resource group filter. +type GroupFilter struct { + TagName *string `json:"tagName,omitempty"` + TagValue *string `json:"tagValue,omitempty"` +} + +// GroupListResult is list of resource groups. +type GroupListResult struct { + autorest.Response `json:"-"` + Value *[]Group `json:"value,omitempty"` + NextLink *string `json:"nextLink,omitempty"` +} + +// GroupListResultPreparer prepares a request to retrieve the next set of results. It returns +// nil if no more results exist. +func (client GroupListResult) GroupListResultPreparer() (*http.Request, error) { + if client.NextLink == nil || len(to.String(client.NextLink)) <= 0 { + return nil, nil + } + return autorest.Prepare(&http.Request{}, + autorest.AsJSON(), + autorest.AsGet(), + autorest.WithBaseURL(to.String(client.NextLink))) +} + +// GroupPatchable is resource group information. +type GroupPatchable struct { + Name *string `json:"name,omitempty"` + Properties *GroupProperties `json:"properties,omitempty"` + ManagedBy *string `json:"managedBy,omitempty"` + Tags *map[string]*string `json:"tags,omitempty"` +} + +// GroupProperties is the resource group properties. +type GroupProperties struct { + ProvisioningState *string `json:"provisioningState,omitempty"` +} + +// HTTPMessage is HTTP message. +type HTTPMessage struct { + Content *map[string]interface{} `json:"content,omitempty"` +} + +// Identity is identity for the resource. +type Identity struct { + PrincipalID *string `json:"principalId,omitempty"` + TenantID *string `json:"tenantId,omitempty"` + Type ResourceIdentityType `json:"type,omitempty"` +} + +// ListResult is list of resource groups. +type ListResult struct { + autorest.Response `json:"-"` + Value *[]GenericResource `json:"value,omitempty"` + NextLink *string `json:"nextLink,omitempty"` +} + +// ListResultPreparer prepares a request to retrieve the next set of results. It returns +// nil if no more results exist. +func (client ListResult) ListResultPreparer() (*http.Request, error) { + if client.NextLink == nil || len(to.String(client.NextLink)) <= 0 { + return nil, nil + } + return autorest.Prepare(&http.Request{}, + autorest.AsJSON(), + autorest.AsGet(), + autorest.WithBaseURL(to.String(client.NextLink))) +} + +// ManagementErrorWithDetails is the detailed error message of resource management. +type ManagementErrorWithDetails struct { + Code *string `json:"code,omitempty"` + Message *string `json:"message,omitempty"` + Target *string `json:"target,omitempty"` + Details *[]ManagementErrorWithDetails `json:"details,omitempty"` +} + +// MoveInfo is parameters of move resources. +type MoveInfo struct { + ResourcesProperty *[]string `json:"resources,omitempty"` + TargetResourceGroup *string `json:"targetResourceGroup,omitempty"` +} + +// ParametersLink is entity representing the reference to the deployment paramaters. +type ParametersLink struct { + URI *string `json:"uri,omitempty"` + ContentVersion *string `json:"contentVersion,omitempty"` +} + +// Plan is plan for the resource. +type Plan struct { + Name *string `json:"name,omitempty"` + Publisher *string `json:"publisher,omitempty"` + Product *string `json:"product,omitempty"` + PromotionCode *string `json:"promotionCode,omitempty"` +} + +// Provider is resource provider information. +type Provider struct { + autorest.Response `json:"-"` + ID *string `json:"id,omitempty"` + Namespace *string `json:"namespace,omitempty"` + RegistrationState *string `json:"registrationState,omitempty"` + ResourceTypes *[]ProviderResourceType `json:"resourceTypes,omitempty"` +} + +// ProviderListResult is list of resource providers. +type ProviderListResult struct { + autorest.Response `json:"-"` + Value *[]Provider `json:"value,omitempty"` + NextLink *string `json:"nextLink,omitempty"` +} + +// ProviderListResultPreparer prepares a request to retrieve the next set of results. It returns +// nil if no more results exist. +func (client ProviderListResult) ProviderListResultPreparer() (*http.Request, error) { + if client.NextLink == nil || len(to.String(client.NextLink)) <= 0 { + return nil, nil + } + return autorest.Prepare(&http.Request{}, + autorest.AsJSON(), + autorest.AsGet(), + autorest.WithBaseURL(to.String(client.NextLink))) +} + +// ProviderOperationDisplayProperties is resource provider operation's display properties. +type ProviderOperationDisplayProperties struct { + Publisher *string `json:"publisher,omitempty"` + Provider *string `json:"provider,omitempty"` + Resource *string `json:"resource,omitempty"` + Operation *string `json:"operation,omitempty"` + Description *string `json:"description,omitempty"` +} + +// ProviderResourceType is resource type managed by the resource provider. +type ProviderResourceType struct { + ResourceType *string `json:"resourceType,omitempty"` + Locations *[]string `json:"locations,omitempty"` + Aliases *[]AliasType `json:"aliases,omitempty"` + APIVersions *[]string `json:"apiVersions,omitempty"` + Properties *map[string]*string `json:"properties,omitempty"` +} + +// Resource is resource. +type Resource struct { + ID *string `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + Type *string `json:"type,omitempty"` + Location *string `json:"location,omitempty"` + Tags *map[string]*string `json:"tags,omitempty"` +} + +// Sku is SKU for the resource. +type Sku struct { + Name *string `json:"name,omitempty"` + Tier *string `json:"tier,omitempty"` + Size *string `json:"size,omitempty"` + Family *string `json:"family,omitempty"` + Model *string `json:"model,omitempty"` + Capacity *int32 `json:"capacity,omitempty"` +} + +// SubResource is sub-resource. +type SubResource struct { + ID *string `json:"id,omitempty"` +} + +// TagCount is tag count. +type TagCount struct { + Type *string `json:"type,omitempty"` + Value *int32 `json:"value,omitempty"` +} + +// TagDetails is tag details. +type TagDetails struct { + autorest.Response `json:"-"` + ID *string `json:"id,omitempty"` + TagName *string `json:"tagName,omitempty"` + Count *TagCount `json:"count,omitempty"` + Values *[]TagValue `json:"values,omitempty"` +} + +// TagsListResult is list of subscription tags. +type TagsListResult struct { + autorest.Response `json:"-"` + Value *[]TagDetails `json:"value,omitempty"` + NextLink *string `json:"nextLink,omitempty"` +} + +// TagsListResultPreparer prepares a request to retrieve the next set of results. It returns +// nil if no more results exist. +func (client TagsListResult) TagsListResultPreparer() (*http.Request, error) { + if client.NextLink == nil || len(to.String(client.NextLink)) <= 0 { + return nil, nil + } + return autorest.Prepare(&http.Request{}, + autorest.AsJSON(), + autorest.AsGet(), + autorest.WithBaseURL(to.String(client.NextLink))) +} + +// TagValue is tag information. +type TagValue struct { + autorest.Response `json:"-"` + ID *string `json:"id,omitempty"` + TagValue *string `json:"tagValue,omitempty"` + Count *TagCount `json:"count,omitempty"` +} + +// TargetResource is target resource. +type TargetResource struct { + ID *string `json:"id,omitempty"` + ResourceName *string `json:"resourceName,omitempty"` + ResourceType *string `json:"resourceType,omitempty"` +} + +// TemplateLink is entity representing the reference to the template. +type TemplateLink struct { + URI *string `json:"uri,omitempty"` + ContentVersion *string `json:"contentVersion,omitempty"` +} diff --git a/cluster-autoscaler/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/resources/providers.go b/cluster-autoscaler/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/resources/providers.go new file mode 100755 index 000000000000..173b4626b247 --- /dev/null +++ b/cluster-autoscaler/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/resources/providers.go @@ -0,0 +1,375 @@ +package resources + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/azure" + "net/http" +) + +// ProvidersClient is the provides operations for working with resources and resource groups. +type ProvidersClient struct { + ManagementClient +} + +// NewProvidersClient creates an instance of the ProvidersClient client. +func NewProvidersClient(subscriptionID string) ProvidersClient { + return NewProvidersClientWithBaseURI(DefaultBaseURI, subscriptionID) +} + +// NewProvidersClientWithBaseURI creates an instance of the ProvidersClient client. +func NewProvidersClientWithBaseURI(baseURI string, subscriptionID string) ProvidersClient { + return ProvidersClient{NewWithBaseURI(baseURI, subscriptionID)} +} + +// Get gets the specified resource provider. +// +// resourceProviderNamespace is the namespace of the resource provider. expand is the $expand query parameter. For +// example, to include property aliases in response, use $expand=resourceTypes/aliases. +func (client ProvidersClient) Get(resourceProviderNamespace string, expand string) (result Provider, err error) { + req, err := client.GetPreparer(resourceProviderNamespace, expand) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.ProvidersClient", "Get", nil, "Failure preparing request") + return + } + + resp, err := client.GetSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "resources.ProvidersClient", "Get", resp, "Failure sending request") + return + } + + result, err = client.GetResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.ProvidersClient", "Get", resp, "Failure responding to request") + } + + return +} + +// GetPreparer prepares the Get request. +func (client ProvidersClient) GetPreparer(resourceProviderNamespace string, expand string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "resourceProviderNamespace": autorest.Encode("path", resourceProviderNamespace), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + if len(expand) > 0 { + queryParameters["$expand"] = autorest.Encode("query", expand) + } + + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/providers/{resourceProviderNamespace}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// GetSender sends the Get request. The method will close the +// http.Response Body if it receives an error. +func (client ProvidersClient) GetSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// GetResponder handles the response to the Get request. The method always +// closes the http.Response Body. +func (client ProvidersClient) GetResponder(resp *http.Response) (result Provider, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// List gets all resource providers for a subscription. +// +// top is the number of results to return. If null is passed returns all deployments. expand is the properties to +// include in the results. For example, use &$expand=metadata in the query string to retrieve resource provider +// metadata. To include property aliases in response, use $expand=resourceTypes/aliases. +func (client ProvidersClient) List(top *int32, expand string) (result ProviderListResult, err error) { + req, err := client.ListPreparer(top, expand) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.ProvidersClient", "List", nil, "Failure preparing request") + return + } + + resp, err := client.ListSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "resources.ProvidersClient", "List", resp, "Failure sending request") + return + } + + result, err = client.ListResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.ProvidersClient", "List", resp, "Failure responding to request") + } + + return +} + +// ListPreparer prepares the List request. +func (client ProvidersClient) ListPreparer(top *int32, expand string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + if top != nil { + queryParameters["$top"] = autorest.Encode("query", *top) + } + if len(expand) > 0 { + queryParameters["$expand"] = autorest.Encode("query", expand) + } + + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/providers", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// ListSender sends the List request. The method will close the +// http.Response Body if it receives an error. +func (client ProvidersClient) ListSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// ListResponder handles the response to the List request. The method always +// closes the http.Response Body. +func (client ProvidersClient) ListResponder(resp *http.Response) (result ProviderListResult, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// ListNextResults retrieves the next set of results, if any. +func (client ProvidersClient) ListNextResults(lastResults ProviderListResult) (result ProviderListResult, err error) { + req, err := lastResults.ProviderListResultPreparer() + if err != nil { + return result, autorest.NewErrorWithError(err, "resources.ProvidersClient", "List", nil, "Failure preparing next results request") + } + if req == nil { + return + } + + resp, err := client.ListSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + return result, autorest.NewErrorWithError(err, "resources.ProvidersClient", "List", resp, "Failure sending next results request") + } + + result, err = client.ListResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.ProvidersClient", "List", resp, "Failure responding to next results request") + } + + return +} + +// ListComplete gets all elements from the list without paging. +func (client ProvidersClient) ListComplete(top *int32, expand string, cancel <-chan struct{}) (<-chan Provider, <-chan error) { + resultChan := make(chan Provider) + errChan := make(chan error, 1) + go func() { + defer func() { + close(resultChan) + close(errChan) + }() + list, err := client.List(top, expand) + if err != nil { + errChan <- err + return + } + if list.Value != nil { + for _, item := range *list.Value { + select { + case <-cancel: + return + case resultChan <- item: + // Intentionally left blank + } + } + } + for list.NextLink != nil { + list, err = client.ListNextResults(list) + if err != nil { + errChan <- err + return + } + if list.Value != nil { + for _, item := range *list.Value { + select { + case <-cancel: + return + case resultChan <- item: + // Intentionally left blank + } + } + } + } + }() + return resultChan, errChan +} + +// Register registers a subscription with a resource provider. +// +// resourceProviderNamespace is the namespace of the resource provider to register. +func (client ProvidersClient) Register(resourceProviderNamespace string) (result Provider, err error) { + req, err := client.RegisterPreparer(resourceProviderNamespace) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.ProvidersClient", "Register", nil, "Failure preparing request") + return + } + + resp, err := client.RegisterSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "resources.ProvidersClient", "Register", resp, "Failure sending request") + return + } + + result, err = client.RegisterResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.ProvidersClient", "Register", resp, "Failure responding to request") + } + + return +} + +// RegisterPreparer prepares the Register request. +func (client ProvidersClient) RegisterPreparer(resourceProviderNamespace string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "resourceProviderNamespace": autorest.Encode("path", resourceProviderNamespace), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsPost(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/providers/{resourceProviderNamespace}/register", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// RegisterSender sends the Register request. The method will close the +// http.Response Body if it receives an error. +func (client ProvidersClient) RegisterSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// RegisterResponder handles the response to the Register request. The method always +// closes the http.Response Body. +func (client ProvidersClient) RegisterResponder(resp *http.Response) (result Provider, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// Unregister unregisters a subscription from a resource provider. +// +// resourceProviderNamespace is the namespace of the resource provider to unregister. +func (client ProvidersClient) Unregister(resourceProviderNamespace string) (result Provider, err error) { + req, err := client.UnregisterPreparer(resourceProviderNamespace) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.ProvidersClient", "Unregister", nil, "Failure preparing request") + return + } + + resp, err := client.UnregisterSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "resources.ProvidersClient", "Unregister", resp, "Failure sending request") + return + } + + result, err = client.UnregisterResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.ProvidersClient", "Unregister", resp, "Failure responding to request") + } + + return +} + +// UnregisterPreparer prepares the Unregister request. +func (client ProvidersClient) UnregisterPreparer(resourceProviderNamespace string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "resourceProviderNamespace": autorest.Encode("path", resourceProviderNamespace), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsPost(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/providers/{resourceProviderNamespace}/unregister", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// UnregisterSender sends the Unregister request. The method will close the +// http.Response Body if it receives an error. +func (client ProvidersClient) UnregisterSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// UnregisterResponder handles the response to the Unregister request. The method always +// closes the http.Response Body. +func (client ProvidersClient) UnregisterResponder(resp *http.Response) (result Provider, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} diff --git a/cluster-autoscaler/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/resources/tags.go b/cluster-autoscaler/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/resources/tags.go new file mode 100755 index 000000000000..a90b390affae --- /dev/null +++ b/cluster-autoscaler/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/resources/tags.go @@ -0,0 +1,426 @@ +package resources + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/azure" + "net/http" +) + +// TagsClient is the provides operations for working with resources and resource groups. +type TagsClient struct { + ManagementClient +} + +// NewTagsClient creates an instance of the TagsClient client. +func NewTagsClient(subscriptionID string) TagsClient { + return NewTagsClientWithBaseURI(DefaultBaseURI, subscriptionID) +} + +// NewTagsClientWithBaseURI creates an instance of the TagsClient client. +func NewTagsClientWithBaseURI(baseURI string, subscriptionID string) TagsClient { + return TagsClient{NewWithBaseURI(baseURI, subscriptionID)} +} + +// CreateOrUpdate the tag name can have a maximum of 512 characters and is case insensitive. Tag names created by Azure +// have prefixes of microsoft, azure, or windows. You cannot create tags with one of these prefixes. +// +// tagName is the name of the tag to create. +func (client TagsClient) CreateOrUpdate(tagName string) (result TagDetails, err error) { + req, err := client.CreateOrUpdatePreparer(tagName) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.TagsClient", "CreateOrUpdate", nil, "Failure preparing request") + return + } + + resp, err := client.CreateOrUpdateSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "resources.TagsClient", "CreateOrUpdate", resp, "Failure sending request") + return + } + + result, err = client.CreateOrUpdateResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.TagsClient", "CreateOrUpdate", resp, "Failure responding to request") + } + + return +} + +// CreateOrUpdatePreparer prepares the CreateOrUpdate request. +func (client TagsClient) CreateOrUpdatePreparer(tagName string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + "tagName": autorest.Encode("path", tagName), + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsPut(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/tagNames/{tagName}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// CreateOrUpdateSender sends the CreateOrUpdate request. The method will close the +// http.Response Body if it receives an error. +func (client TagsClient) CreateOrUpdateSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// CreateOrUpdateResponder handles the response to the CreateOrUpdate request. The method always +// closes the http.Response Body. +func (client TagsClient) CreateOrUpdateResponder(resp *http.Response) (result TagDetails, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusCreated), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// CreateOrUpdateValue creates a tag value. The name of the tag must already exist. +// +// tagName is the name of the tag. tagValue is the value of the tag to create. +func (client TagsClient) CreateOrUpdateValue(tagName string, tagValue string) (result TagValue, err error) { + req, err := client.CreateOrUpdateValuePreparer(tagName, tagValue) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.TagsClient", "CreateOrUpdateValue", nil, "Failure preparing request") + return + } + + resp, err := client.CreateOrUpdateValueSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "resources.TagsClient", "CreateOrUpdateValue", resp, "Failure sending request") + return + } + + result, err = client.CreateOrUpdateValueResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.TagsClient", "CreateOrUpdateValue", resp, "Failure responding to request") + } + + return +} + +// CreateOrUpdateValuePreparer prepares the CreateOrUpdateValue request. +func (client TagsClient) CreateOrUpdateValuePreparer(tagName string, tagValue string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + "tagName": autorest.Encode("path", tagName), + "tagValue": autorest.Encode("path", tagValue), + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsPut(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/tagNames/{tagName}/tagValues/{tagValue}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// CreateOrUpdateValueSender sends the CreateOrUpdateValue request. The method will close the +// http.Response Body if it receives an error. +func (client TagsClient) CreateOrUpdateValueSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// CreateOrUpdateValueResponder handles the response to the CreateOrUpdateValue request. The method always +// closes the http.Response Body. +func (client TagsClient) CreateOrUpdateValueResponder(resp *http.Response) (result TagValue, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusCreated), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// Delete you must remove all values from a resource tag before you can delete it. +// +// tagName is the name of the tag. +func (client TagsClient) Delete(tagName string) (result autorest.Response, err error) { + req, err := client.DeletePreparer(tagName) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.TagsClient", "Delete", nil, "Failure preparing request") + return + } + + resp, err := client.DeleteSender(req) + if err != nil { + result.Response = resp + err = autorest.NewErrorWithError(err, "resources.TagsClient", "Delete", resp, "Failure sending request") + return + } + + result, err = client.DeleteResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.TagsClient", "Delete", resp, "Failure responding to request") + } + + return +} + +// DeletePreparer prepares the Delete request. +func (client TagsClient) DeletePreparer(tagName string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + "tagName": autorest.Encode("path", tagName), + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsDelete(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/tagNames/{tagName}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// DeleteSender sends the Delete request. The method will close the +// http.Response Body if it receives an error. +func (client TagsClient) DeleteSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// DeleteResponder handles the response to the Delete request. The method always +// closes the http.Response Body. +func (client TagsClient) DeleteResponder(resp *http.Response) (result autorest.Response, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusNoContent), + autorest.ByClosing()) + result.Response = resp + return +} + +// DeleteValue deletes a tag value. +// +// tagName is the name of the tag. tagValue is the value of the tag to delete. +func (client TagsClient) DeleteValue(tagName string, tagValue string) (result autorest.Response, err error) { + req, err := client.DeleteValuePreparer(tagName, tagValue) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.TagsClient", "DeleteValue", nil, "Failure preparing request") + return + } + + resp, err := client.DeleteValueSender(req) + if err != nil { + result.Response = resp + err = autorest.NewErrorWithError(err, "resources.TagsClient", "DeleteValue", resp, "Failure sending request") + return + } + + result, err = client.DeleteValueResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.TagsClient", "DeleteValue", resp, "Failure responding to request") + } + + return +} + +// DeleteValuePreparer prepares the DeleteValue request. +func (client TagsClient) DeleteValuePreparer(tagName string, tagValue string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + "tagName": autorest.Encode("path", tagName), + "tagValue": autorest.Encode("path", tagValue), + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsDelete(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/tagNames/{tagName}/tagValues/{tagValue}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// DeleteValueSender sends the DeleteValue request. The method will close the +// http.Response Body if it receives an error. +func (client TagsClient) DeleteValueSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// DeleteValueResponder handles the response to the DeleteValue request. The method always +// closes the http.Response Body. +func (client TagsClient) DeleteValueResponder(resp *http.Response) (result autorest.Response, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusNoContent), + autorest.ByClosing()) + result.Response = resp + return +} + +// List gets the names and values of all resource tags that are defined in a subscription. +func (client TagsClient) List() (result TagsListResult, err error) { + req, err := client.ListPreparer() + if err != nil { + err = autorest.NewErrorWithError(err, "resources.TagsClient", "List", nil, "Failure preparing request") + return + } + + resp, err := client.ListSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "resources.TagsClient", "List", resp, "Failure sending request") + return + } + + result, err = client.ListResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.TagsClient", "List", resp, "Failure responding to request") + } + + return +} + +// ListPreparer prepares the List request. +func (client TagsClient) ListPreparer() (*http.Request, error) { + pathParameters := map[string]interface{}{ + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2017-05-10" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/tagNames", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// ListSender sends the List request. The method will close the +// http.Response Body if it receives an error. +func (client TagsClient) ListSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// ListResponder handles the response to the List request. The method always +// closes the http.Response Body. +func (client TagsClient) ListResponder(resp *http.Response) (result TagsListResult, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// ListNextResults retrieves the next set of results, if any. +func (client TagsClient) ListNextResults(lastResults TagsListResult) (result TagsListResult, err error) { + req, err := lastResults.TagsListResultPreparer() + if err != nil { + return result, autorest.NewErrorWithError(err, "resources.TagsClient", "List", nil, "Failure preparing next results request") + } + if req == nil { + return + } + + resp, err := client.ListSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + return result, autorest.NewErrorWithError(err, "resources.TagsClient", "List", resp, "Failure sending next results request") + } + + result, err = client.ListResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "resources.TagsClient", "List", resp, "Failure responding to next results request") + } + + return +} + +// ListComplete gets all elements from the list without paging. +func (client TagsClient) ListComplete(cancel <-chan struct{}) (<-chan TagDetails, <-chan error) { + resultChan := make(chan TagDetails) + errChan := make(chan error, 1) + go func() { + defer func() { + close(resultChan) + close(errChan) + }() + list, err := client.List() + if err != nil { + errChan <- err + return + } + if list.Value != nil { + for _, item := range *list.Value { + select { + case <-cancel: + return + case resultChan <- item: + // Intentionally left blank + } + } + } + for list.NextLink != nil { + list, err = client.ListNextResults(list) + if err != nil { + errChan <- err + return + } + if list.Value != nil { + for _, item := range *list.Value { + select { + case <-cancel: + return + case resultChan <- item: + // Intentionally left blank + } + } + } + } + }() + return resultChan, errChan +} diff --git a/cluster-autoscaler/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/resources/version.go b/cluster-autoscaler/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/resources/version.go new file mode 100755 index 000000000000..f1ff686a7f4b --- /dev/null +++ b/cluster-autoscaler/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/resources/version.go @@ -0,0 +1,28 @@ +package resources + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// UserAgent returns the UserAgent string to use when sending http.Requests. +func UserAgent() string { + return "Azure-SDK-For-Go/v11.0.0-beta arm-resources/2017-05-10" +} + +// Version returns the semantic version (see http://semver.org) of the client. +func Version() string { + return "v11.0.0-beta" +}