Merge pull request #514 from feiskyer/node-pools

Add support for Azure virtual machine availability sets (VMAS)
This commit is contained in:
Marcin Wielgus 2017-12-29 20:51:58 +01:00 committed by GitHub
commit c24580a55b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 6969 additions and 853 deletions

View File

@ -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",

View File

@ -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/<subscription-id>" --output json
```
And fill the values with the result you got into the configmap
## Deployment manifests
```yaml
apiVersion: v1
data:
ClientID: <client-id>
ClientSecret: <client-secret>
ResourceGroup: <resource-group>
SubscriptionID: <subscription-id>
TenantID: <tenand-id>
ScaleSetName: <scale-set-name>
kind: ConfigMap
metadata:
name: cluster-autoscaler-azure
namespace: kube-system
```
### VMSS deployment
Create the configmap by running
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
- ClientID: `<base64-encoded-client-id>`
- ClientSecret: `<base64-encoded-client-secret>`
- ResourceGroup: `<base64-encoded-resource-group>`
- SubscriptionID: `<base64-encode-subscription-id>`
- TenantID: `<base64-encoded-tenant-id>`
- NodeGroup: `<base64-encoded-scale-set-name>`
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"
```
## Deploy in master node
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-vmss-master.yaml
```
### Standard deployment
Pre-requirements:
- Get credentials from above `permissions` step.
- Get the required paramters from acs-engine deployments (usually under directory `_output/<master-dns-prefix>` 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: `<base64-encoded-client-id>`
- ClientSecret: `<base64-encoded-client-secret>`
- ResourceGroup: `<base64-encoded-resource-group>`
- SubscriptionID: `<base64-encode-subscription-id>`
- TenantID: `<base64-encoded-tenant-id>`
- NodeGroup: `<base64-encoded-node-pool-name>`
- Deployment: `<base64-encoded-azure-initial-deploy-name>`
- APIServerPrivateKey: `<base64-encoded-apiserver-private-key>`
- CAPrivateKey: `<base64-encoded-ca-private-key>`
- ClientPrivateKey: `<base64-encoded-client-private-key>`
- KubeConfigPrivateKey: `<base64-encoded-kubeconfig-private-key>`
- WindowsAdminPassword: `<base64-encoded-windows-admin-password>`
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.
```sh
kubectl create -f cluster-autoscaler-standard-master.yaml
```

View File

@ -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
}

View File

@ -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,6 +126,46 @@ func (azure *AzureCloudProvider) Refresh() error {
return nil
}
// Create nodeGroup from provided spec.
// spec is in the following format: min-size:max-size:scale-set-name.
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)
}
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)
}
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 < minSize {
return nil, fmt.Errorf("max size must be greater or equal to min size")
}
maxSize = size
} else {
return nil, fmt.Errorf("failed to set max size: %s, expected integer", tokens[1])
}
if tokens[2] == "" {
return nil, fmt.Errorf("scale set name must not be blank, got spec: %s", spec)
}
if azure.azureManager.config.VMType == vmTypeStandard {
return NewAgentPool(name, minSize, maxSize, azure.azureManager)
}
return NewScaleSet(name, minSize, maxSize, azure.azureManager)
}
// AzureRef contains a reference to some entity in Azure world.
type AzureRef struct {
Name string
@ -137,207 +175,3 @@ type AzureRef struct {
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:///<unique-id>, 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.
// spec is in the following format: min-size:max-size:scale-set-name.
func buildScaleSet(spec string, azureManager *AzureManager) (*ScaleSet, error) {
tokens := strings.SplitN(spec, ":", 3)
if len(tokens) != 3 {
return nil, fmt.Errorf("wrong nodes configuration: %s", spec)
}
scaleSet := ScaleSet{
azureManager: azureManager,
}
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
} 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 {
return nil, fmt.Errorf("max size must be greater or equal to min size")
}
scaleSet.maxSize = size
} else {
return nil, fmt.Errorf("failed to set max size: %s, expected integer", tokens[1])
}
if tokens[2] == "" {
return nil, fmt.Errorf("scale set name must not be blank, got spec: %s", spec)
}
scaleSet.Name = tokens[2]
return &scaleSet, nil
}
// Nodes returns a list of all nodes that belong to this node group.
func (scaleSet *ScaleSet) Nodes() ([]string, error) {
return scaleSet.azureManager.GetScaleSetVms(scaleSet)
}

View File

@ -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,
}
return compute.VirtualMachineScaleSetVMListResult{
Value: &value,
}, nil
}
var testAzureManager = &AzureManager{
scaleSets: make([]*scaleSetInformation, 0),
scaleSetClient: &VirtualMachineScaleSetsClientMock{},
scaleSetVmClient: &VirtualMachineScaleSetVMsClientMock{},
scaleSetCache: make(map[AzureRef]*ScaleSet),
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{},
}
}
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")
func TestBuildNodeGroup(t *testing.T) {
provider, err := newTestProvider()
assert.NoError(t, err)
_, err = provider.buildNodeGroup("a")
assert.Error(t, err)
_, err = AzureRefFromProviderId("azure://test/rg/test-instance-id")
_, err = provider.buildNodeGroup("a:b:c")
assert.Error(t, err)
_, err = provider.buildNodeGroup("1:")
assert.Error(t, err)
_, err = provider.buildNodeGroup("1:2:")
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)
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)
_, err = provider.buildNodeGroup("-1:2:")
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)
assert.Error(t, err)
_, err = buildScaleSet("a:b:c", nil)
assert.Error(t, err)
_, err = buildScaleSet("1:", nil)
assert.Error(t, err)
_, err = buildScaleSet("1:2:", nil)
_, err = provider.buildNodeGroup("5:3:")
assert.Error(t, err)
_, err = buildScaleSet("-1:2:", nil)
_, err = provider.buildNodeGroup("5:ddd:test-name")
assert.Error(t, err)
_, err = buildScaleSet("5:3:", nil)
assert.Error(t, err)
_, err = buildScaleSet("5:ddd:test-name", nil)
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())
}

View File

@ -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
}

View File

@ -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)
}
// 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)
}
// 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)
}
// AzureManager handles Azure communication and data caching.
type AzureManager struct {
resourceGroupName string
subscription string
scaleSetClient scaleSetClient
scaleSetVmClient scaleSetVMClient
config *Config
env azure.Environment
scaleSets []*scaleSetInformation
scaleSetCache map[AzureRef]*ScaleSet
virtualMachineScaleSetsClient VirtualMachineScaleSetsClient
virtualMachineScaleSetVMsClient VirtualMachineScaleSetVMsClient
virtualMachinesClient VirtualMachinesClient
deploymentsClient DeploymentsClient
interfacesClient InterfacesClient
disksClient DisksClient
storageAccountsClient AccountsClient
// cache of mapping from instance id to the scale set id
scaleSetIdCache map[string]string
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{}
@ -73,98 +118,179 @@ type Config struct {
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"`
VMType string `json:"vmType" yaml:"vmType"`
AADClientID string `json:"aadClientId" yaml:"aadClientId"`
AADClientSecret string `json:"aadClientSecret" yaml:"aadClientSecret"`
AADTenantID string `json:"aadTenantId" yaml:"aadTenantId"`
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
if configReader != nil {
var err error
var cfg Config
if configReader != nil {
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")
if resourceGroup == "" {
panic("Resource group not found")
}
if subscriptionId == "" {
panic("Subscription ID not found")
}
if tenantId == "" {
panic("Tenant ID not found.")
}
if clientId == "" {
panic("ARM Client ID not found")
}
if clientSecret == "" {
panic("ARM Client Secret not found.")
}
glog.Infof("read configuration: %v", subscriptionId)
spt, err := NewServicePrincipalTokenFromCredentials(tenantId, clientId, clientSecret, azure.PublicCloud.ServiceManagementEndpoint)
useManagedIdentityExtensionFromEnv := os.Getenv("ARM_USE_MANAGED_IDENTITY_EXTENSION")
if len(useManagedIdentityExtensionFromEnv) > 0 {
cfg.UseManagedIdentityExtension, err = strconv.ParseBool(useManagedIdentityExtensionFromEnv)
if err != nil {
panic(err)
return nil, err
}
}
}
scaleSetAPI = compute.NewVirtualMachineScaleSetsClient(subscriptionId)
scaleSetsClient := scaleSetAPI.(compute.VirtualMachineScaleSetsClient)
// 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 cfg.ResourceGroup == "" {
return nil, fmt.Errorf("resource group not set")
}
if cfg.SubscriptionID == "" {
return nil, fmt.Errorf("subscription ID not set")
}
if cfg.TenantID == "" {
return nil, fmt.Errorf("tenant ID not set")
}
if cfg.AADClientID == "" {
return nil, fmt.Errorf("ARM Client ID not set")
}
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("Starting azure manager with subscription ID %q", cfg.SubscriptionID)
spt, err := NewServicePrincipalTokenFromCredentials(&cfg, &env)
if err != nil {
return nil, err
}
scaleSetsClient := compute.NewVirtualMachineScaleSetsClient(cfg.SubscriptionID)
scaleSetsClient.BaseURI = env.ResourceManagerEndpoint
scaleSetsClient.Authorizer = autorest.NewBearerAuthorizer(spt)
scaleSetsClient.Sender = autorest.CreateSender()
scaleSetsClient.PollingDelay = 5 * time.Second
configureUserAgent(&scaleSetsClient.Client)
glog.V(5).Infof("Created scale set client with authorizer: %v", scaleSetsClient)
glog.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()
scaleSetVMsClient.PollingDelay = 5 * time.Second
configureUserAgent(&scaleSetVMsClient.Client)
glog.V(5).Infof("Created scale set vm client with authorizer: %v", scaleSetVMsClient)
glog.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)
// Create Availability Sets Azure Client.
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),
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)
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)
}
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)
}
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)
})
}
}
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)
})
}
}
// 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,
})
m.nodeGroups = append(m.nodeGroups, nodeGroup)
}
// 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
func (m *AzureManager) nodeGroupRegisted(nodeGroup string) bool {
for _, ng := range m.nodeGroups {
if nodeGroup == ng.Id() {
return true
}
glog.V(5).Infof("Returning scale set capacity: %d\n", *set.Sku.Capacity)
return *set.Sku.Capacity, nil
}
// 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
}
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
}
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())
}
}
// 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.scaleSetIdCache[instance.Name]
instanceIds[i] = m.instanceIDsCache[instance.Name]
}
requiredIds := &compute.VirtualMachineScaleSetVMInstanceRequiredIDs{
InstanceIds: &instanceIds,
}
cancel := make(chan struct{})
_, errChan := m.scaleSetClient.DeleteInstances(m.resourceGroupName, commonAsg.Name, *requiredIds, cancel)
return <-errChan
return instanceIds
}
func (m *AzureManager) regenerateCache() error {
newCache := make(map[AzureRef]*ScaleSet)
newScaleSetIdCache := make(map[string]string)
func (m *AzureManager) regenerateCache() (err error) {
var newCache map[AzureRef]cloudprovider.NodeGroup
var newInstanceIDsCache 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
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)
}
sset.basename = *scaleSet.Name
result, err := m.scaleSetVmClient.List(m.resourceGroupName, sset.basename, "", "", "")
if err != nil {
glog.Errorf("Failed to list vm for scaleSet %s: %v", sset.config.Name, err)
return 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,
}
newCache[ref] = sset.config
newScaleSetIdCache[name] = *instance.InstanceID
}
}
m.scaleSetCache = newCache
m.scaleSetIdCache = newScaleSetIdCache
m.nodeGroupsCache = newCache
m.instanceIDsCache = newInstanceIDsCache
return 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, "", "", "")
if err != nil {
glog.V(4).Infof("Failed AS info request for %s: %v", scaleSet.Name, err)
return []string{}, err
func (m *AzureManager) getNodeGroupByID(id string) cloudprovider.NodeGroup {
for _, ng := range m.nodeGroups {
if id == ng.Id() {
return ng
}
result := make([]string, 0)
for _, instance := range *instances.Value {
}
return nil
}
func (m *AzureManager) listAgentPools() (map[AzureRef]cloudprovider.NodeGroup, map[string]string, error) {
as := make(map[AzureRef]cloudprovider.NodeGroup)
instanceIDs := make(map[string]string)
for _, nodeGroup := range m.nodeGroups {
agentPool, ok := nodeGroup.(*AgentPool)
if !ok {
return nil, nil, fmt.Errorf("node group %q is not AgentPool", nodeGroup)
}
_, vmIndex, err := agentPool.GetVMIndexes()
if err != nil {
glog.Errorf("GetVMIndexes for node group %q failed: %v", nodeGroup.Id(), err)
return nil, nil, err
}
for idx := range vmIndex {
id := vmIndex[idx].ID
vmID := vmIndex[idx].VMID
idRef := AzureRef{
Name: id,
}
vmIDRef := AzureRef{
Name: vmID,
}
as[idRef] = nodeGroup
as[vmIDRef] = nodeGroup
instanceIDs[id] = fmt.Sprintf("%d", idx)
instanceIDs[vmID] = fmt.Sprintf("%d", idx)
}
}
return as, instanceIDs, nil
}
// 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)
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)
result = append(result, name)
vmID := "azure://" + strings.ToLower(*instance.VMID)
ref := AzureRef{
Name: name,
}
vmIDRef := AzureRef{
Name: vmID,
}
scaleSets[ref] = sset
scaleSets[vmIDRef] = sset
instanceIDs[name] = *instance.InstanceID
}
return result, nil
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 scaleSets, instanceIDs, err
}
// Cleanup closes the channel to signal the go routine to stop that is handling the cache

View File

@ -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()
}

View File

@ -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)")
}

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -0,0 +1,137 @@
apiVersion: v1
data:
ClientID: <base64-encoded-client-id>
ClientSecret: <base64-encoded-client-secret>
ResourceGroup: <base64-encoded-resource-group>
SubscriptionID: <base64-encode-subscription-id>
TenantID: <base64-encoded-tenant-id>
NodeGroup: <base64-encoded-node-pool-name>
Deployment: <base64-encoded-azure-initial-deploy-name>
APIServerPrivateKey: <base64-encoded-apiserver-private-key>
CAPrivateKey: <base64-encoded-ca-private-key>
ClientPrivateKey: <base64-encoded-client-private-key>
KubeConfigPrivateKey: <base64-encoded-kubeconfig-private-key>
WindowsAdminPassword: <base64-encoded-windows-admin-password>
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

View File

@ -0,0 +1,132 @@
apiVersion: v1
data:
ClientID: <base64-encoded-client-id>
ClientSecret: <base64-encoded-client-secret>
ResourceGroup: <base64-encoded-resource-group>
SubscriptionID: <base64-encode-subscription-id>
TenantID: <base64-encoded-tenant-id>
NodeGroup: <base64-encoded-node-pool-name>
Deployment: <base64-encoded-azure-initial-deploy-name>
APIServerPrivateKey: <base64-encoded-apiserver-private-key>
CAPrivateKey: <base64-encoded-ca-private-key>
ClientPrivateKey: <base64-encoded-client-private-key>
KubeConfigPrivateKey: <base64-encoded-kubeconfig-private-key>
WindowsAdminPassword: <base64-encoded-windows-admin-password>
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

View File

@ -0,0 +1,100 @@
apiVersion: v1
data:
ClientID: <base64-encoded-client-id>
ClientSecret: <base64-encoded-client-secret>
ResourceGroup: <base64-encoded-resource-group>
SubscriptionID: <base64-encode-subscription-id>
TenantID: <base64-encoded-tenant-id>
NodeGroup: <base64-encoded-scale-set-name>
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

View File

@ -0,0 +1,95 @@
apiVersion: v1
data:
ClientID: <base64-encoded-client-id>
ClientSecret: <base64-encoded-client-secret>
ResourceGroup: <base64-encoded-resource-group>
SubscriptionID: <base64-encode-subscription-id>
TenantID: <base64-encoded-tenant-id>
NodeGroup: <base64-encoded-scale-set-name>
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

View File

@ -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,
}
}

View File

@ -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
}

View File

@ -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
}

File diff suppressed because it is too large Load Diff

View File

@ -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
}

View File

@ -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"`
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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"
}