16625: a-d-c: add support for Azure managed images. Update our packer
[arvados.git] / lib / cloud / azure / azure_test.go
index e924aa61063d506f394b29798177dd9dff0ee2ab..186966e19ccaffc6f2126168a3bca3482f4aca18 100644 (file)
@@ -3,31 +3,35 @@
 // SPDX-License-Identifier: AGPL-3.0
 //
 //
-// How to manually run individual tests against the real cloud
+// How to manually run individual tests against the real cloud:
 //
-// $ go test -v git.curoverse.com/arvados.git/lib/cloud -live-azure-cfg azconfig.yml -check.f=TestListInstances
+// $ go test -v git.arvados.org/arvados.git/lib/cloud/azure -live-azure-cfg azconfig.yml -check.f=TestCreate
+//
+// Tests should be run individually and in the order they are listed in the file:
 //
 // Example azconfig.yml:
 //
-// subscription_id: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
-// key: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
-// region: centralus
-// cloud_environment: AzurePublicCloud
-// secret: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-// tenant_id: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
-// resource_group: zzzzz
-// network: zzzzz
-// subnet: zzzzz-subnet-private
-// storage_account: example
-// blob_container: vhds
-// image: "https://example.blob.core.windows.net/system/Microsoft.Compute/Images/images/zzzzz-compute-osDisk.XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.vhd"
-// delete_dangling_resources_after: 20
-// authorized_key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDLQS1ExT2+WjA0d/hntEAyAtgeN1W2ik2QX8c2zO6HjlPHWXL92r07W0WMuDib40Pcevpi1BXeBWXA9ZB5KKMJB+ukaAu22KklnQuUmNvk6ZXnPKSkGxuCYvPQb08WhHf3p1VxiKfP3iauedBDM4x9/bkJohlBBQiFXzNUcQ+a6rKiMzmJN2gbL8ncyUzc+XQ5q4JndTwTGtOlzDiGOc9O4z5Dd76wtAVJneOuuNpwfFRVHThpJM6VThpCZOnl8APaceWXKeuwOuCae3COZMz++xQfxOfZ9Z8aIwo+TlQhsRaNfZ4Vjrop6ej8dtfZtgUFKfbXEOYaHrGrWGotFDTD example@example"
+// ImageIDForTestSuite: "https://example.blob.core.windows.net/system/Microsoft.Compute/Images/images/zzzzz-compute-osDisk.XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.vhd"
+// DriverParameters:
+//      SubscriptionID: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
+//      ClientID: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
+//      Location: centralus
+//      CloudEnvironment: AzurePublicCloud
+//      ClientSecret: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+//      TenantId: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
+//      ResourceGroup: zzzzz
+//      Network: zzzzz
+//      Subnet: zzzzz-subnet-private
+//      StorageAccount: example
+//      BlobContainer: vhds
+//      DeleteDanglingResourcesAfter: 20s
+//      AdminUsername: crunch
 
 package azure
 
 import (
        "context"
+       "encoding/json"
        "errors"
        "flag"
        "io/ioutil"
@@ -35,19 +39,20 @@ import (
        "net"
        "net/http"
        "os"
+       "strings"
        "testing"
        "time"
 
-       "git.curoverse.com/arvados.git/lib/cloud"
-       "git.curoverse.com/arvados.git/sdk/go/arvados"
-       "git.curoverse.com/arvados.git/sdk/go/config"
-       "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-06-01/compute"
+       "git.arvados.org/arvados.git/lib/cloud"
+       "git.arvados.org/arvados.git/lib/dispatchcloud/test"
+       "git.arvados.org/arvados.git/sdk/go/arvados"
+       "git.arvados.org/arvados.git/sdk/go/config"
+       "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute"
        "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-06-01/network"
        "github.com/Azure/azure-sdk-for-go/storage"
        "github.com/Azure/go-autorest/autorest"
        "github.com/Azure/go-autorest/autorest/azure"
        "github.com/Azure/go-autorest/autorest/to"
-       "github.com/jmcvetta/randutil"
        "github.com/sirupsen/logrus"
        "golang.org/x/crypto/ssh"
        check "gopkg.in/check.v1"
@@ -62,11 +67,11 @@ type AzureInstanceSetSuite struct{}
 
 var _ = check.Suite(&AzureInstanceSetSuite{})
 
-type VirtualMachinesClientStub struct{}
+const testNamePrefix = "compute-test123-"
 
-var testKey []byte = []byte(`ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDLQS1ExT2+WjA0d/hntEAyAtgeN1W2ik2QX8c2zO6HjlPHWXL92r07W0WMuDib40Pcevpi1BXeBWXA9ZB5KKMJB+ukaAu22KklnQuUmNvk6ZXnPKSkGxuCYvPQb08WhHf3p1VxiKfP3iauedBDM4x9/bkJohlBBQiFXzNUcQ+a6rKiMzmJN2gbL8ncyUzc+XQ5q4JndTwTGtOlzDiGOc9O4z5Dd76wtAVJneOuuNpwfFRVHThpJM6VThpCZOnl8APaceWXKeuwOuCae3COZMz++xQfxOfZ9Z8aIwo+TlQhsRaNfZ4Vjrop6ej8dtfZtgUFKfbXEOYaHrGrWGotFDTD example@example`)
+type VirtualMachinesClientStub struct{}
 
-func (*VirtualMachinesClientStub) CreateOrUpdate(ctx context.Context,
+func (*VirtualMachinesClientStub) createOrUpdate(ctx context.Context,
        resourceGroupName string,
        VMName string,
        parameters compute.VirtualMachine) (result compute.VirtualMachine, err error) {
@@ -75,17 +80,17 @@ func (*VirtualMachinesClientStub) CreateOrUpdate(ctx context.Context,
        return parameters, nil
 }
 
-func (*VirtualMachinesClientStub) Delete(ctx context.Context, resourceGroupName string, VMName string) (result *http.Response, err error) {
+func (*VirtualMachinesClientStub) delete(ctx context.Context, resourceGroupName string, VMName string) (result *http.Response, err error) {
        return nil, nil
 }
 
-func (*VirtualMachinesClientStub) ListComplete(ctx context.Context, resourceGroupName string) (result compute.VirtualMachineListResultIterator, err error) {
+func (*VirtualMachinesClientStub) listComplete(ctx context.Context, resourceGroupName string) (result compute.VirtualMachineListResultIterator, err error) {
        return compute.VirtualMachineListResultIterator{}, nil
 }
 
 type InterfacesClientStub struct{}
 
-func (*InterfacesClientStub) CreateOrUpdate(ctx context.Context,
+func (*InterfacesClientStub) createOrUpdate(ctx context.Context,
        resourceGroupName string,
        nicName string,
        parameters network.Interface) (result network.Interface, err error) {
@@ -94,14 +99,29 @@ func (*InterfacesClientStub) CreateOrUpdate(ctx context.Context,
        return parameters, nil
 }
 
-func (*InterfacesClientStub) Delete(ctx context.Context, resourceGroupName string, VMName string) (result *http.Response, err error) {
+func (*InterfacesClientStub) delete(ctx context.Context, resourceGroupName string, VMName string) (result *http.Response, err error) {
        return nil, nil
 }
 
-func (*InterfacesClientStub) ListComplete(ctx context.Context, resourceGroupName string) (result network.InterfaceListResultIterator, err error) {
+func (*InterfacesClientStub) listComplete(ctx context.Context, resourceGroupName string) (result network.InterfaceListResultIterator, err error) {
        return network.InterfaceListResultIterator{}, nil
 }
 
+type BlobContainerStub struct{}
+
+func (*BlobContainerStub) GetBlobReference(name string) *storage.Blob {
+       return nil
+}
+
+func (*BlobContainerStub) ListBlobs(params storage.ListBlobsParameters) (storage.BlobListResponse, error) {
+       return storage.BlobListResponse{}, nil
+}
+
+type testConfig struct {
+       ImageIDForTestSuite string
+       DriverParameters    json.RawMessage
+}
+
 var live = flag.String("live-azure-cfg", "", "Test with real azure API, provide config file")
 
 func GetInstanceSet() (cloud.InstanceSet, cloud.ImageID, arvados.Cluster, error) {
@@ -118,29 +138,30 @@ func GetInstanceSet() (cloud.InstanceSet, cloud.ImageID, arvados.Cluster, error)
                        },
                })}
        if *live != "" {
-               cfg := make(map[string]interface{})
-               err := config.LoadFile(&cfg, *live)
+               var exampleCfg testConfig
+               err := config.LoadFile(&exampleCfg, *live)
                if err != nil {
                        return nil, cloud.ImageID(""), cluster, err
                }
-               ap, err := NewAzureInstanceSet(cfg, "test123", logrus.StandardLogger())
-               return ap, cloud.ImageID(cfg["image"].(string)), cluster, err
-       } else {
-               ap := AzureInstanceSet{
-                       azconfig: AzureInstanceSetConfig{
-                               BlobContainer: "vhds",
-                       },
-                       dispatcherID: "test123",
-                       namePrefix:   "compute-test123-",
-                       logger:       logrus.StandardLogger(),
-                       deleteNIC:    make(chan string),
-                       deleteBlob:   make(chan storage.Blob),
-               }
-               ap.ctx, ap.stopFunc = context.WithCancel(context.Background())
-               ap.vmClient = &VirtualMachinesClientStub{}
-               ap.netClient = &InterfacesClientStub{}
-               return &ap, cloud.ImageID("blob"), cluster, nil
+
+               ap, err := newAzureInstanceSet(exampleCfg.DriverParameters, "test123", nil, logrus.StandardLogger())
+               return ap, cloud.ImageID(exampleCfg.ImageIDForTestSuite), cluster, err
+       }
+       ap := azureInstanceSet{
+               azconfig: azureInstanceSetConfig{
+                       BlobContainer: "vhds",
+               },
+               dispatcherID: "test123",
+               namePrefix:   testNamePrefix,
+               logger:       logrus.StandardLogger(),
+               deleteNIC:    make(chan string),
+               deleteBlob:   make(chan storage.Blob),
        }
+       ap.ctx, ap.stopFunc = context.WithCancel(context.Background())
+       ap.vmClient = &VirtualMachinesClientStub{}
+       ap.netClient = &InterfacesClientStub{}
+       ap.blobcont = &BlobContainerStub{}
+       return &ap, cloud.ImageID("blob"), cluster, nil
 }
 
 func (*AzureInstanceSetSuite) TestCreate(c *check.C) {
@@ -149,21 +170,19 @@ func (*AzureInstanceSetSuite) TestCreate(c *check.C) {
                c.Fatal("Error making provider", err)
        }
 
-       pk, _, _, _, err := ssh.ParseAuthorizedKey(testKey)
-       c.Assert(err, check.IsNil)
-
-       nodetoken, err := randutil.String(40, "abcdefghijklmnopqrstuvwxyz0123456789")
+       pk, _ := test.LoadTestKey(c, "../../dispatchcloud/test/sshkey_dispatch")
        c.Assert(err, check.IsNil)
 
        inst, err := ap.Create(cluster.InstanceTypes["tiny"],
                img, map[string]string{
-                       "node-token": nodetoken},
-               pk)
+                       "TestTagName": "test tag value",
+               }, "umask 0600; echo -n test-file-data >/var/run/test-file", pk)
 
        c.Assert(err, check.IsNil)
 
-       tg := inst.Tags()
-       log.Printf("Result %v %v %v", inst.String(), inst.Address(), tg)
+       tags := inst.Tags()
+       c.Check(tags["TestTagName"], check.Equals, "test tag value")
+       c.Logf("inst.String()=%v Address()=%v Tags()=%v", inst.String(), inst.Address(), tags)
 
 }
 
@@ -189,7 +208,7 @@ func (*AzureInstanceSetSuite) TestManageNics(c *check.C) {
                c.Fatal("Error making provider", err)
        }
 
-       ap.(*AzureInstanceSet).ManageNics()
+       ap.(*azureInstanceSet).manageNics()
        ap.Stop()
 }
 
@@ -199,7 +218,7 @@ func (*AzureInstanceSetSuite) TestManageBlobs(c *check.C) {
                c.Fatal("Error making provider", err)
        }
 
-       ap.(*AzureInstanceSet).ManageBlobs()
+       ap.(*azureInstanceSet).manageBlobs()
        ap.Stop()
 }
 
@@ -212,7 +231,7 @@ func (*AzureInstanceSetSuite) TestDestroyInstances(c *check.C) {
        l, err := ap.Instances(nil)
        c.Assert(err, check.IsNil)
 
-       for _, i := range l {
+       for _, i := range filterInstances(c, l) {
                c.Check(i.Destroy(), check.IsNil)
        }
 }
@@ -223,7 +242,7 @@ func (*AzureInstanceSetSuite) TestDeleteFake(c *check.C) {
                c.Fatal("Error making provider", err)
        }
 
-       _, err = ap.(*AzureInstanceSet).netClient.Delete(context.Background(), "fakefakefake", "fakefakefake")
+       _, err = ap.(*azureInstanceSet).netClient.delete(context.Background(), "fakefakefake", "fakefakefake")
 
        de, ok := err.(autorest.DetailedError)
        if ok {
@@ -245,7 +264,7 @@ func (*AzureInstanceSetSuite) TestWrapError(c *check.C) {
                        ServiceError: &azure.ServiceError{},
                },
        }
-       wrapped := WrapAzureError(retryError)
+       wrapped := wrapAzureError(retryError)
        _, ok := wrapped.(cloud.RateLimitError)
        c.Check(ok, check.Equals, true)
 
@@ -261,7 +280,7 @@ func (*AzureInstanceSetSuite) TestWrapError(c *check.C) {
                        },
                },
        }
-       wrapped = WrapAzureError(quotaError)
+       wrapped = wrapAzureError(quotaError)
        _, ok = wrapped.(cloud.QuotaError)
        c.Check(ok, check.Equals, true)
 }
@@ -271,17 +290,20 @@ func (*AzureInstanceSetSuite) TestSetTags(c *check.C) {
        if err != nil {
                c.Fatal("Error making provider", err)
        }
+
        l, err := ap.Instances(nil)
        c.Assert(err, check.IsNil)
-
+       l = filterInstances(c, l)
        if len(l) > 0 {
                err = l[0].SetTags(map[string]string{"foo": "bar"})
                if err != nil {
                        c.Fatal("Error setting tags", err)
                }
        }
+
        l, err = ap.Instances(nil)
        c.Assert(err, check.IsNil)
+       l = filterInstances(c, l)
 
        if len(l) > 0 {
                tg := l[0].Tags()
@@ -296,21 +318,25 @@ func (*AzureInstanceSetSuite) TestSSH(c *check.C) {
        }
        l, err := ap.Instances(nil)
        c.Assert(err, check.IsNil)
+       l = filterInstances(c, l)
 
        if len(l) > 0 {
-
                sshclient, err := SetupSSHClient(c, l[0])
                c.Assert(err, check.IsNil)
+               defer sshclient.Conn.Close()
 
                sess, err := sshclient.NewSession()
                c.Assert(err, check.IsNil)
-
-               out, err := sess.Output("cat /home/crunch/node-token")
+               defer sess.Close()
+               _, err = sess.Output("find /var/run/test-file -maxdepth 0 -user root -perm 0600")
                c.Assert(err, check.IsNil)
 
-               log.Printf("%v", string(out))
-
-               sshclient.Conn.Close()
+               sess, err = sshclient.NewSession()
+               c.Assert(err, check.IsNil)
+               defer sess.Close()
+               out, err := sess.Output("sudo cat /var/run/test-file")
+               c.Assert(err, check.IsNil)
+               c.Check(string(out), check.Equals, "test-file-data")
        }
 }
 
@@ -353,3 +379,15 @@ func SetupSSHClient(c *check.C, inst cloud.Instance) (*ssh.Client, error) {
 
        return client, nil
 }
+
+func filterInstances(c *check.C, instances []cloud.Instance) []cloud.Instance {
+       var r []cloud.Instance
+       for _, i := range instances {
+               if !strings.HasPrefix(i.String(), testNamePrefix) {
+                       c.Logf("ignoring instance %s", i)
+                       continue
+               }
+               r = append(r, i)
+       }
+       return r
+}