13964: Can successfully create a VM, list existing VMs
authorPeter Amstutz <pamstutz@veritasgenetics.com>
Tue, 21 Aug 2018 15:28:37 +0000 (11:28 -0400)
committerPeter Amstutz <pamstutz@veritasgenetics.com>
Wed, 9 Jan 2019 21:28:15 +0000 (16:28 -0500)
Cleanup unused NICs.  Blob cleanup in progress.

Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <pamstutz@veritasgenetics.com>

lib/dispatchcloud/azure.go
lib/dispatchcloud/provider.go
vendor/vendor.json

index e397731e3ee666a9ab29d371a192bf4d39dc315a..6f769bfaaa766bf91af23ea54c1ce63425369832 100644 (file)
@@ -1,29 +1,43 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
 package dispatchcloud
 
 import (
        "context"
        "fmt"
+       "log"
        "net/http"
+       "sync"
+       "time"
 
        "git.curoverse.com/arvados.git/sdk/go/arvados"
        "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-06-01/compute"
        "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-06-01/network"
+       storageacct "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2018-02-01/storage"
+       "github.com/Azure/azure-sdk-for-go/storage"
        "github.com/Azure/go-autorest/autorest/azure"
        "github.com/Azure/go-autorest/autorest/azure/auth"
        "github.com/Azure/go-autorest/autorest/to"
+       "github.com/jmcvetta/randutil"
 )
 
 type AzureProviderConfig struct {
-       SubscriptionID string
-       ClientID       string
-       ClientSecret   string
-       TenantID       string
-       CloudEnv       string
-       ResourceGroup  string
-       Location       string
-       Subnet         string
-       StorageAccount string
-       BlobContainer  string
+       SubscriptionID               string  `json:"subscription_id"`
+       ClientID                     string  `json:"key"`
+       ClientSecret                 string  `json:"secret"`
+       TenantID                     string  `json:"tenant_id"`
+       CloudEnv                     string  `json:"cloud_environment"`
+       ResourceGroup                string  `json:"resource_group"`
+       Location                     string  `json:"region"`
+       Network                      string  `json:"network"`
+       Subnet                       string  `json:"subnet"`
+       StorageAccount               string  `json:"storage_account"`
+       BlobContainer                string  `json:"blob_container"`
+       Image                        string  `json:"image"`
+       AuthorizedKey                string  `json:"authorized_key"`
+       DeleteDanglingResourcesAfter float64 `json:"delete_dangling_resources_after"`
 }
 
 type VirtualMachinesClientWrapper interface {
@@ -32,7 +46,7 @@ type VirtualMachinesClientWrapper interface {
                VMName string,
                parameters compute.VirtualMachine) (result compute.VirtualMachine, err error)
        Delete(ctx context.Context, resourceGroupName string, VMName string) (result *http.Response, err error)
-       List(ctx context.Context, resourceGroupName string) (result compute.VirtualMachineListResultPage, err error)
+       ListComplete(ctx context.Context, resourceGroupName string) (result compute.VirtualMachineListResultIterator, err error)
 }
 
 type VirtualMachinesClientImpl struct {
@@ -42,11 +56,11 @@ type VirtualMachinesClientImpl struct {
 func (cl *VirtualMachinesClientImpl) CreateOrUpdate(ctx context.Context,
        resourceGroupName string,
        VMName string,
-       parameters VirtualMachine) (result compute.VirtualMachine, err error) {
+       parameters compute.VirtualMachine) (result compute.VirtualMachine, err error) {
 
        future, err := cl.inner.CreateOrUpdate(ctx, resourceGroupName, VMName, parameters)
        if err != nil {
-               return nil, err
+               return compute.VirtualMachine{}, err
        }
        future.WaitForCompletionRef(ctx, cl.inner.Client)
        return future.Result(cl.inner)
@@ -57,12 +71,12 @@ func (cl *VirtualMachinesClientImpl) Delete(ctx context.Context, resourceGroupNa
        if err != nil {
                return nil, err
        }
-       future.WaitForCompletionRef(ctx, cl.inner.Client)
-       return future.GetResult()
+       err = future.WaitForCompletionRef(ctx, cl.inner.Client)
+       return future.Response(), err
 }
 
-func (cl *VirtualMachinesClientImpl) List(ctx context.Context, resourceGroupName string) (result compute.VirtualMachineListResultPage, err error) {
-       return cl.inner.List(ctx, resourceGroupName)
+func (cl *VirtualMachinesClientImpl) ListComplete(ctx context.Context, resourceGroupName string) (result compute.VirtualMachineListResultIterator, err error) {
+       return cl.inner.ListComplete(ctx, resourceGroupName)
 }
 
 type InterfacesClientWrapper interface {
@@ -71,7 +85,7 @@ type InterfacesClientWrapper interface {
                networkInterfaceName string,
                parameters network.Interface) (result network.Interface, err error)
        Delete(ctx context.Context, resourceGroupName string, networkInterfaceName string) (result *http.Response, err error)
-       List(ctx context.Context, resourceGroupName string) (result network.InterfaceListResultPage, err error)
+       ListComplete(ctx context.Context, resourceGroupName string) (result network.InterfaceListResultIterator, err error)
 }
 
 type InterfacesClientImpl struct {
@@ -83,50 +97,63 @@ func (cl *InterfacesClientImpl) Delete(ctx context.Context, resourceGroupName st
        if err != nil {
                return nil, err
        }
-       future.WaitForCompletionRef(ctx, cl.inner.Client)
-       return future.GetResult()
+       err = future.WaitForCompletionRef(ctx, cl.inner.Client)
+       return future.Response(), err
 }
 
 func (cl *InterfacesClientImpl) CreateOrUpdate(ctx context.Context,
        resourceGroupName string,
        networkInterfaceName string,
-       parameters network.Interface) (result compute.VirtualMachine, err error) {
+       parameters network.Interface) (result network.Interface, err error) {
 
        future, err := cl.inner.CreateOrUpdate(ctx, resourceGroupName, networkInterfaceName, parameters)
        if err != nil {
-               return nil, err
+               return network.Interface{}, err
        }
        future.WaitForCompletionRef(ctx, cl.inner.Client)
        return future.Result(cl.inner)
 }
 
-func (cl *InterfacesClientImpl) List(ctx context.Context, resourceGroupName string) (result compute.InterfaceListResultPage, err error) {
-       return cl.inner.List(ctx, resourceGroupName)
+func (cl *InterfacesClientImpl) ListComplete(ctx context.Context, resourceGroupName string) (result network.InterfaceListResultIterator, err error) {
+       return cl.inner.ListComplete(ctx, resourceGroupName)
 }
 
 type AzureProvider struct {
-       config    AzureProviderConfig
-       vmClient  VirtualMachinesClientWrapper
-       netClient InterfacesClientWrapper
-       azureEnv  auth.Environment
+       azconfig          AzureProviderConfig
+       arvconfig         arvados.Cluster
+       vmClient          VirtualMachinesClientWrapper
+       netClient         InterfacesClientWrapper
+       storageAcctClient storageacct.AccountsClient
+       azureEnv          azure.Environment
+}
+
+func NewAzureProvider(azcfg AzureProviderConfig, arvcfg arvados.Cluster) (prv Provider, err error) {
+       ap := AzureProvider{}
+       err = ap.setup(azcfg, arvcfg)
+       if err != nil {
+               return nil, err
+       }
+       return &ap, nil
 }
 
-func (az *AzureProvider) Init(cfg AzureProviderConfig) error {
-       az.config = cfg
-       vmClient := compute.NewVirtualMachinesClient(az.config.SubscriptionId)
-       netClient := network.NewInterfacesClient(az.config.SubscriptionId)
+func (az *AzureProvider) setup(azcfg AzureProviderConfig, arvcfg arvados.Cluster) (err error) {
+       az.azconfig = azcfg
+       az.arvconfig = arvcfg
+       vmClient := compute.NewVirtualMachinesClient(az.azconfig.SubscriptionID)
+       netClient := network.NewInterfacesClient(az.azconfig.SubscriptionID)
+       storageAcctClient := storageacct.NewAccountsClient(az.azconfig.SubscriptionID)
 
-       az.azureEnv, err = azure.EnvironmentFromName(az.config.CloudEnv)
+       az.azureEnv, err = azure.EnvironmentFromName(az.azconfig.CloudEnv)
        if err != nil {
                return err
        }
 
        authorizer, err := auth.ClientCredentialsConfig{
-               ClientID:     az.config.ClientID,
-               ClientSecret: az.config.ClientSecret,
-               TenantID:     az.config.TenantID,
-               Resource:     env.ResourceManagerEndpoint,
-               AADEndpoint:  env.ActiveDirectoryEndpoint,
+               ClientID:     az.azconfig.ClientID,
+               ClientSecret: az.azconfig.ClientSecret,
+               TenantID:     az.azconfig.TenantID,
+               Resource:     az.azureEnv.ResourceManagerEndpoint,
+               AADEndpoint:  az.azureEnv.ActiveDirectoryEndpoint,
        }.Authorizer()
        if err != nil {
                return err
@@ -134,9 +161,11 @@ func (az *AzureProvider) Init(cfg AzureProviderConfig) error {
 
        vmClient.Authorizer = authorizer
        netClient.Authorizer = authorizer
+       storageAcctClient.Authorizer = authorizer
 
-       az.vmClient = VirtualMachinesClientImpl{vmClient}
-       az.netClient = InterfacesClientImpl{netClient}
+       az.vmClient = &VirtualMachinesClientImpl{vmClient}
+       az.netClient = &InterfacesClientImpl{netClient}
+       az.storageAcctClient = storageAcctClient
 
        return nil
 }
@@ -146,65 +175,85 @@ func (az *AzureProvider) Create(ctx context.Context,
        imageId ImageID,
        instanceTag []InstanceTag) (Instance, error) {
 
-       name := "randomname"
+       name, err := randutil.String(15, "abcdefghijklmnopqrstuvwxyz0123456789")
+       if err != nil {
+               return nil, err
+       }
+
+       name = "compute-" + name
+       log.Printf("name is %v", name)
+
+       timestamp := time.Now().Format(time.RFC3339Nano)
 
        nicParameters := network.Interface{
-               Location: az.config.Location,
-               Tags: []map[string]string{
-                       "arvados-class":   "dynamic-compute",
-                       "arvados-cluster": "",
+               Location: &az.azconfig.Location,
+               Tags: map[string]*string{
+                       "arvados-class":   to.StringPtr("crunch-dynamic-compute"),
+                       "arvados-cluster": &az.arvconfig.ClusterID,
+                       "created-at":      &timestamp,
                },
                InterfacePropertiesFormat: &network.InterfacePropertiesFormat{
                        IPConfigurations: &[]network.InterfaceIPConfiguration{
-                               network.InterfaceIPConfiguration{},
-                               Name: "ip1",
-                               InterfaceIPConfigurationPropertiesFormat: &network.InterfaceIPConfigurationPropertiesFormat{
-                                       Subnet: &network.Subnet{
-                                               ID: az.config.Subnet,
+                               network.InterfaceIPConfiguration{
+                                       Name: to.StringPtr("ip1"),
+                                       InterfaceIPConfigurationPropertiesFormat: &network.InterfaceIPConfigurationPropertiesFormat{
+                                               Subnet: &network.Subnet{
+                                                       ID: to.StringPtr(fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers"+
+                                                               "/Microsoft.Network/virtualnetworks/%s/subnets/%s",
+                                                               az.azconfig.SubscriptionID,
+                                                               az.azconfig.ResourceGroup,
+                                                               az.azconfig.Network,
+                                                               az.azconfig.Subnet)),
+                                               },
+                                               PrivateIPAllocationMethod: network.Dynamic,
                                        },
-                                       PrivateIPAllocationMethod: network.Dynamic,
                                },
                        },
                },
        }
-       nic, err := az.netClient.CreateOrUpdate(ctx, az.config.ResourceGroup, name+"-nic", nicParameters)
+       nic, err := az.netClient.CreateOrUpdate(ctx, az.azconfig.ResourceGroup, name+"-nic", nicParameters)
        if err != nil {
                return nil, err
        }
 
-       instance_vhd = fmt.Sprintf("https://%s.blob.%s/%s/%s-os.vhd",
-               az.config.StorageAccount,
+       log.Printf("Created NIC %v", *nic.ID)
+
+       instance_vhd := fmt.Sprintf("https://%s.blob.%s/%s/%s-os.vhd",
+               az.azconfig.StorageAccount,
                az.azureEnv.StorageEndpointSuffix,
-               az.config.BlobContainer,
+               az.azconfig.BlobContainer,
                name)
 
+       log.Printf("URI instance vhd %v", instance_vhd)
+
        vmParameters := compute.VirtualMachine{
-               Location: az.config.Location,
-               Tags: []map[string]string{
-                       "arvados-class":   "dynamic-compute",
-                       "arvados-cluster": "",
+               Location: &az.azconfig.Location,
+               Tags: map[string]*string{
+                       "arvados-class":   to.StringPtr("crunch-dynamic-compute"),
+                       "arvados-cluster": &az.arvconfig.ClusterID,
+                       "created-at":      &timestamp,
                },
                VirtualMachineProperties: &compute.VirtualMachineProperties{
                        HardwareProfile: &compute.HardwareProfile{
-                               VMSize: instanceType.ProviderType,
+                               VMSize: compute.VirtualMachineSizeTypes(instanceType.ProviderType),
                        },
                        StorageProfile: &compute.StorageProfile{
                                OsDisk: &compute.OSDisk{
                                        OsType:       compute.Linux,
-                                       Name:         "",
+                                       Name:         to.StringPtr(fmt.Sprintf("%v-os", name)),
                                        CreateOption: compute.FromImage,
                                        Image: &compute.VirtualHardDisk{
-                                               URI: imageId,
+                                               URI: to.StringPtr(string(imageId)),
                                        },
                                        Vhd: &compute.VirtualHardDisk{
-                                               URI: vhd,
+                                               URI: &instance_vhd,
                                        },
                                },
                        },
                        NetworkProfile: &compute.NetworkProfile{
                                NetworkInterfaces: &[]compute.NetworkInterfaceReference{
                                        compute.NetworkInterfaceReference{
-                                               ID: "",
+                                               ID: nic.ID,
                                                NetworkInterfaceReferenceProperties: &compute.NetworkInterfaceReferenceProperties{
                                                        Primary: to.BoolPtr(true),
                                                },
@@ -212,22 +261,25 @@ func (az *AzureProvider) Create(ctx context.Context,
                                },
                        },
                        OsProfile: &compute.OSProfile{
+                               ComputerName:  &name,
+                               AdminUsername: to.StringPtr("arvados"),
                                LinuxConfiguration: &compute.LinuxConfiguration{
-                                       DisablePasswordAuthentication: true,
+                                       DisablePasswordAuthentication: to.BoolPtr(true),
                                        SSH: &compute.SSHConfiguration{
                                                PublicKeys: &[]compute.SSHPublicKey{
                                                        compute.SSHPublicKey{
-                                                               Path:    "",
-                                                               KeyData: "",
+                                                               Path:    to.StringPtr("/home/arvados/.ssh/authorized_keys"),
+                                                               KeyData: to.StringPtr(az.azconfig.AuthorizedKey),
                                                        },
                                                },
                                        },
                                },
+                               //CustomData: to.StringPtr(""),
                        },
                },
        }
 
-       vm, err := az.vmClient.CreateOrUpdate(ctx, az.config.ResourceGroup, name+"-compute", vmParameters)
+       vm, err := az.vmClient.CreateOrUpdate(ctx, az.azconfig.ResourceGroup, name+"-compute", vmParameters)
        if err != nil {
                return nil, err
        }
@@ -237,22 +289,113 @@ func (az *AzureProvider) Create(ctx context.Context,
                provider:     az,
                nic:          nic,
                vm:           vm,
-       }
+       }, nil
 }
 
 func (az *AzureProvider) Instances(ctx context.Context) ([]Instance, error) {
-       result, err := az.vmClient.List(ctx, az.config.ResourceGroup)
+       result, err := az.vmClient.ListComplete(ctx, az.azconfig.ResourceGroup)
        if err != nil {
                return nil, err
        }
-       instances := make([]Instance)
+
+       instances := make([]Instance, 0)
+
+       for ; result.NotDone(); err = result.Next() {
+               if err != nil {
+                       return nil, err
+               }
+               log.Printf("%v", *result.Value().Name)
+               if result.Value().Tags["arvados-class"] != nil &&
+                       (*result.Value().Tags["arvados-class"]) == "crunch-dynamic-compute" {
+                       instances = append(instances, &AzureInstance{
+                               provider: az,
+                               vm:       result.Value()})
+               }
+       }
+       return instances, nil
+}
+
+func (az *AzureProvider) DeleteDanglingNics(ctx context.Context) {
+       result, err := az.netClient.ListComplete(ctx, az.azconfig.ResourceGroup)
+       if err != nil {
+               return
+       }
+
+       timestamp := time.Now()
+       wg := sync.WaitGroup{}
+       defer wg.Wait()
+       for ; result.NotDone(); err = result.Next() {
+               if err != nil {
+                       log.Printf("Error listing nics: %v", err)
+                       return
+               }
+               if !result.NotDone() {
+                       return
+               }
+               if result.Value().Tags["arvados-class"] != nil &&
+                       (*result.Value().Tags["arvados-class"]) == "crunch-dynamic-compute" &&
+                       result.Value().VirtualMachine == nil {
+
+                       if result.Value().Tags["created-at"] != nil {
+                               created_at, err := time.Parse(time.RFC3339Nano, *result.Value().Tags["created-at"])
+                               if err == nil {
+                                       log.Printf("found dangling NIC %v created %v seconds ago", *result.Value().Name, timestamp.Sub(created_at).Seconds())
+                                       if timestamp.Sub(created_at).Seconds() > az.azconfig.DeleteDanglingResourcesAfter {
+                                               log.Printf("Will delete %v because it is older than %v s", *result.Value().Name, az.azconfig.DeleteDanglingResourcesAfter)
+                                               wg.Add(1)
+                                               go func(nicname string) {
+                                                       r, delerr := az.netClient.Delete(context.Background(), az.azconfig.ResourceGroup, nicname)
+                                                       log.Printf("%v %v", r, delerr)
+                                                       wg.Done()
+                                               }(*result.Value().Name)
+                                       }
+                               }
+                       }
+               }
+       }
+
+}
+
+func (az *AzureProvider) DeleteDanglingBlobs(ctx context.Context) {
+       result, err := az.storageAcctClient.ListKeys(ctx, az.azconfig.ResourceGroup, az.azconfig.StorageAccount)
+       if err != nil {
+               log.Printf("Couldn't get account keys %v", err)
+               return
+       }
+
+       key1 := *(*result.Keys)[0].Value
+       client, err := storage.NewBasicClientOnSovereignCloud(az.azconfig.StorageAccount, key1, az.azureEnv)
+       if err != nil {
+               log.Printf("Couldn't make client %v", err)
+               return
+       }
+
+       blobsvc := client.GetBlobService()
+       blobcont := blobsvc.GetContainerReference(az.azconfig.BlobContainer)
+
+       timestamp := time.Now()
+       page := storage.ListBlobsParameters{Prefix: "compute-"}
+
        for {
-               if result.NotDone() {
-                       result.Next()
+               response, err := blobcont.ListBlobs(page)
+               if err != nil {
+                       log.Printf("Error listing blobs %v", err)
+                       return
+               }
+               for _, b := range response.Blobs {
+                       age := timestamp.Sub(time.Time(b.Properties.LastModified))
+                       if b.Properties.BlobType == storage.BlobTypePage &&
+                               b.Properties.LeaseState == "available" &&
+                               b.Properties.LeaseStatus == "unlocked" &&
+                               age.Seconds() > az.azconfig.DeleteDanglingResourcesAfter {
+                               log.Printf("Blob %v is unlocked and not modified for %v seconds, will delete", b.Name, age.Seconds())
+                       }
+               }
+               if response.NextMarker != "" {
+                       page.Marker = response.NextMarker
                } else {
-                       return instances, nil
+                       break
                }
-               result.Values
        }
 }
 
@@ -264,11 +407,11 @@ type AzureInstance struct {
 }
 
 func (ai *AzureInstance) String() string {
-       return ai.vm.ID
+       return *ai.vm.ID
 }
 
 func (ai *AzureInstance) ProviderType() string {
-       return ai.vm.VirtualMachineProperties.HardwareProfile.VMSize
+       return string(ai.vm.VirtualMachineProperties.HardwareProfile.VMSize)
 }
 
 func (ai *AzureInstance) InstanceType() arvados.InstanceType {
@@ -279,12 +422,16 @@ func (ai *AzureInstance) SetTags([]InstanceTag) error {
        return nil
 }
 
+func (ai *AzureInstance) GetTags() ([]InstanceTag, error) {
+       return nil, nil
+}
+
 func (ai *AzureInstance) Destroy(ctx context.Context) error {
-       response, err := ai.provider.vm.Delete(ctx, ai.provider.config.ResourceGroup, ai.vm.Name)
+       _, err := ai.provider.vmClient.Delete(ctx, ai.provider.azconfig.ResourceGroup, *ai.vm.Name)
        // check response code
        return err
 }
 
 func (ai *AzureInstance) Address() string {
-       return ai.nic.IPConfigurations[0].PrivateIPAddress
+       return *(*ai.nic.IPConfigurations)[0].PrivateIPAddress
 }
index aebb93011e1ef7d161c195f5da73424462d43dba..e896ac650158660e8135f9402005299d0cee2faa 100644 (file)
@@ -1,3 +1,7 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
 package dispatchcloud
 
 import (
@@ -17,6 +21,8 @@ type Instance interface {
        // Cloud provider's "instance type" ID. Matches a key in
        // configured arvados.InstanceTypeMap.
        ProviderType() string
+       // Get tags
+       GetTags() ([]InstanceTag, error)
        // Replace tags with the given tags
        SetTags([]InstanceTag) error
        // Shut down the node
@@ -26,6 +32,6 @@ type Instance interface {
 }
 
 type Provider interface {
-       Create(arvados.InstanceType, ImageID, []InstanceTag) (Instance, error)
-       Instances() ([]Instance, error)
+       Create(context.Context, arvados.InstanceType, ImageID, []InstanceTag) (Instance, error)
+       Instances(context.Context) ([]Instance, error)
 }
index ec296d21d1c25a92c14639c36658c4b0aa10bce5..6bf8c016d1137e35e777ef79634fd109650cdadd 100644 (file)
                        "revision": "888b4804f2653cd35ebcc95f046079e63b5b2799",
                        "revisionTime": "2017-07-27T13:52:37Z"
                },
+               {
+                       "checksumSHA1": "KF4DsRUpZ+h+qRQ/umRAQZfVvw0=",
+                       "path": "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-06-01/compute",
+                       "revision": "4e8cbbfb1aeab140cd0fa97fd16b64ee18c3ca6a",
+                       "revisionTime": "2018-07-27T22:05:59Z"
+               },
+               {
+                       "checksumSHA1": "IZNzp1cYx+xYHd4gzosKpG6Jr/k=",
+                       "path": "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-06-01/network",
+                       "revision": "4e8cbbfb1aeab140cd0fa97fd16b64ee18c3ca6a",
+                       "revisionTime": "2018-07-27T22:05:59Z"
+               },
+               {
+                       "checksumSHA1": "W4c2uTDJlwhfryWg9esshmJANo0=",
+                       "path": "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2018-02-01/storage",
+                       "revision": "4e8cbbfb1aeab140cd0fa97fd16b64ee18c3ca6a",
+                       "revisionTime": "2018-07-27T22:05:59Z"
+               },
                {
                        "checksumSHA1": "xHZe/h/tyrqmS9qiR03bLfRv5FI=",
                        "path": "github.com/Azure/azure-sdk-for-go/storage",
                        "revisionTime": "2018-02-14T01:17:07Z"
                },
                {
-                       "checksumSHA1": "LQWU/2M2E4L/hVzT9BVW1SkLrpA=",
+                       "checksumSHA1": "1Y2+bSzYrdPHQqRjR1OrBMHAvxY=",
                        "path": "github.com/Azure/go-autorest/autorest",
-                       "revision": "a91c94d19d5efcb398b3aab64b8766e724aa7442",
-                       "revisionTime": "2017-11-30T17:00:06Z"
+                       "revision": "39013ecb48eaf6ced3f4e3e1d95515140ce6b3cf",
+                       "revisionTime": "2018-08-09T20:19:59Z"
                },
                {
-                       "checksumSHA1": "nBQ7cdhoeYUur6G6HG97uueoDmE=",
+                       "checksumSHA1": "GxL0HHpZDj2milPhR3SPV6MWLPc=",
                        "path": "github.com/Azure/go-autorest/autorest/adal",
-                       "revision": "a91c94d19d5efcb398b3aab64b8766e724aa7442",
-                       "revisionTime": "2017-11-30T17:00:06Z"
+                       "revision": "39013ecb48eaf6ced3f4e3e1d95515140ce6b3cf",
+                       "revisionTime": "2018-08-09T20:19:59Z"
                },
                {
-                       "checksumSHA1": "zXyLmDVpkYkIsL0yinNLoW82IZc=",
+                       "checksumSHA1": "ZNgwJOdHZmm4k/HJIbT1L5giO6M=",
                        "path": "github.com/Azure/go-autorest/autorest/azure",
-                       "revision": "a91c94d19d5efcb398b3aab64b8766e724aa7442",
-                       "revisionTime": "2017-11-30T17:00:06Z"
+                       "revision": "39013ecb48eaf6ced3f4e3e1d95515140ce6b3cf",
+                       "revisionTime": "2018-08-09T20:19:59Z"
+               },
+               {
+                       "checksumSHA1": "6i7kwcXGTn55WqfubQs21swgr34=",
+                       "path": "github.com/Azure/go-autorest/autorest/azure/auth",
+                       "revision": "39013ecb48eaf6ced3f4e3e1d95515140ce6b3cf",
+                       "revisionTime": "2018-08-09T20:19:59Z"
                },
                {
                        "checksumSHA1": "9nXCi9qQsYjxCeajJKWttxgEt0I=",
                        "path": "github.com/Azure/go-autorest/autorest/date",
-                       "revision": "a91c94d19d5efcb398b3aab64b8766e724aa7442",
-                       "revisionTime": "2017-11-30T17:00:06Z"
+                       "revision": "39013ecb48eaf6ced3f4e3e1d95515140ce6b3cf",
+                       "revisionTime": "2018-08-09T20:19:59Z"
+               },
+               {
+                       "checksumSHA1": "SbBb2GcJNm5GjuPKGL2777QywR4=",
+                       "path": "github.com/Azure/go-autorest/autorest/to",
+                       "revision": "39013ecb48eaf6ced3f4e3e1d95515140ce6b3cf",
+                       "revisionTime": "2018-08-09T20:19:59Z"
+               },
+               {
+                       "checksumSHA1": "HjdLfAF3oA2In8F3FKh/Y+BPyXk=",
+                       "path": "github.com/Azure/go-autorest/autorest/validation",
+                       "revision": "39013ecb48eaf6ced3f4e3e1d95515140ce6b3cf",
+                       "revisionTime": "2018-08-09T20:19:59Z"
+               },
+               {
+                       "checksumSHA1": "b2lrPJRxf+MEfmMafN40wepi5WM=",
+                       "path": "github.com/Azure/go-autorest/logger",
+                       "revision": "39013ecb48eaf6ced3f4e3e1d95515140ce6b3cf",
+                       "revisionTime": "2018-08-09T20:19:59Z"
+               },
+               {
+                       "checksumSHA1": "UtAIMAsMWLBJ6yO1qZ0soFnb0sI=",
+                       "path": "github.com/Azure/go-autorest/version",
+                       "revision": "39013ecb48eaf6ced3f4e3e1d95515140ce6b3cf",
+                       "revisionTime": "2018-08-09T20:19:59Z"
                },
                {
                        "checksumSHA1": "o/3cn04KAiwC7NqNVvmfVTD+hgA=",
                        "revision": "dbeaa9332f19a944acb5736b4456cfcc02140e29",
                        "revisionTime": "2017-10-19T21:57:19Z"
                },
+               {
+                       "checksumSHA1": "7EjxkAUND/QY/sN+2fNKJ52v1Rc=",
+                       "path": "github.com/dimchansky/utfbom",
+                       "revision": "5448fe645cb1964ba70ac8f9f2ffe975e61a536c",
+                       "revisionTime": "2018-07-13T13:37:17Z"
+               },
                {
                        "checksumSHA1": "Gj+xR1VgFKKmFXYOJMnAczC3Znk=",
                        "path": "github.com/docker/distribution/digestset",
                        "revision": "0fcca4842a8d74bfddc2c96a073bd2a4d2a7a2e8",
                        "revisionTime": "2017-11-25T19:00:56Z"
                },
+               {
+                       "checksumSHA1": "PJY7uCr3UnX4/Mf/RoWnbieSZ8o=",
+                       "path": "golang.org/x/crypto/pkcs12",
+                       "revision": "614d502a4dac94afa3a6ce146bd1736da82514c6",
+                       "revisionTime": "2018-07-28T08:01:47Z"
+               },
+               {
+                       "checksumSHA1": "p0GC51McIdA7JygoP223twJ1s0E=",
+                       "path": "golang.org/x/crypto/pkcs12/internal/rc2",
+                       "revision": "614d502a4dac94afa3a6ce146bd1736da82514c6",
+                       "revisionTime": "2018-07-28T08:01:47Z"
+               },
                {
                        "checksumSHA1": "NHjGg73p5iGZ+7tflJ4cVABNmKE=",
                        "path": "golang.org/x/crypto/ssh",