[ARVADOS] updated: 1.2.0-40-ge8baef326
Git user
git at public.curoverse.com
Fri Aug 31 11:43:50 EDT 2018
Summary of changes:
lib/dispatchcloud/azure.go | 58 ++++++++++++------------
lib/dispatchcloud/azure_test.go | 98 ++++++++++++++++++++++++++++++++++++++---
lib/dispatchcloud/provider.go | 34 +++++++++++---
3 files changed, 147 insertions(+), 43 deletions(-)
via e8baef326a98de0fd14144b1c221bb9c98bc1095 (commit)
from 54701e0e492064eb05bf707d4c1886442cb066de (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
commit e8baef326a98de0fd14144b1c221bb9c98bc1095
Author: Peter Amstutz <pamstutz at veritasgenetics.com>
Date: Fri Aug 31 11:42:01 2018 -0400
13964: SSH access WIP
Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <pamstutz at veritasgenetics.com>
diff --git a/lib/dispatchcloud/azure.go b/lib/dispatchcloud/azure.go
index 21e8d28d8..da2e3eb8f 100644
--- a/lib/dispatchcloud/azure.go
+++ b/lib/dispatchcloud/azure.go
@@ -25,6 +25,7 @@ import (
"github.com/Azure/go-autorest/autorest/azure/auth"
"github.com/Azure/go-autorest/autorest/to"
"github.com/jmcvetta/randutil"
+ "golang.org/x/crypto/ssh"
)
type AzureProviderConfig struct {
@@ -40,7 +41,6 @@ type AzureProviderConfig struct {
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"`
}
@@ -186,7 +186,6 @@ func WrapAzureError(err error) error {
type AzureProvider struct {
azconfig AzureProviderConfig
- arvconfig arvados.Cluster
vmClient VirtualMachinesClientWrapper
netClient InterfacesClientWrapper
storageAcctClient storageacct.AccountsClient
@@ -196,18 +195,17 @@ type AzureProvider struct {
namePrefix string
}
-func NewAzureProvider(azcfg AzureProviderConfig, arvcfg arvados.Cluster, dispatcherID string) (prv Provider, err error) {
+func NewAzureProvider(azcfg AzureProviderConfig, dispatcherID string) (prv InstanceProvider, err error) {
ap := AzureProvider{}
- err = ap.setup(azcfg, arvcfg, dispatcherID)
+ err = ap.setup(azcfg, dispatcherID)
if err != nil {
return nil, err
}
return &ap, nil
}
-func (az *AzureProvider) setup(azcfg AzureProviderConfig, arvcfg arvados.Cluster, dispatcherID string) (err error) {
+func (az *AzureProvider) setup(azcfg AzureProviderConfig, dispatcherID string) (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)
@@ -245,7 +243,8 @@ func (az *AzureProvider) setup(azcfg AzureProviderConfig, arvcfg arvados.Cluster
func (az *AzureProvider) Create(ctx context.Context,
instanceType arvados.InstanceType,
imageId ImageID,
- newTags InstanceTags) (Instance, error) {
+ newTags InstanceTags,
+ publicKey ssh.PublicKey) (Instance, error) {
name, err := randutil.String(15, "abcdefghijklmnopqrstuvwxyz0123456789")
if err != nil {
@@ -300,8 +299,6 @@ func (az *AzureProvider) Create(ctx context.Context,
log.Printf("URI instance vhd %v", instance_vhd)
- tags["arvados-instance-type"] = &instanceType.Name
-
vmParameters := compute.VirtualMachine{
Location: &az.azconfig.Location,
Tags: tags,
@@ -334,14 +331,14 @@ func (az *AzureProvider) Create(ctx context.Context,
},
OsProfile: &compute.OSProfile{
ComputerName: &name,
- AdminUsername: to.StringPtr("arvados"),
+ AdminUsername: to.StringPtr("crunch"),
LinuxConfiguration: &compute.LinuxConfiguration{
DisablePasswordAuthentication: to.BoolPtr(true),
SSH: &compute.SSHConfiguration{
PublicKeys: &[]compute.SSHPublicKey{
compute.SSHPublicKey{
- Path: to.StringPtr("/home/arvados/.ssh/authorized_keys"),
- KeyData: to.StringPtr(az.azconfig.AuthorizedKey),
+ Path: to.StringPtr("/home/crunch/.ssh/authorized_keys"),
+ KeyData: to.StringPtr(string(ssh.MarshalAuthorizedKey(publicKey))),
},
},
},
@@ -357,10 +354,9 @@ func (az *AzureProvider) Create(ctx context.Context,
}
return &AzureInstance{
- instanceType: instanceType,
- provider: az,
- nic: nic,
- vm: vm,
+ provider: az,
+ nic: nic,
+ vm: vm,
}, nil
}
@@ -381,13 +377,11 @@ func (az *AzureProvider) Instances(ctx context.Context) ([]Instance, error) {
if err != nil {
return nil, WrapAzureError(err)
}
- if strings.HasPrefix(*result.Value().Name, az.namePrefix) &&
- result.Value().Tags["arvados-instance-type"] != nil {
+ if strings.HasPrefix(*result.Value().Name, az.namePrefix) {
instances = append(instances, &AzureInstance{
- provider: az,
- vm: result.Value(),
- nic: interfaces[*(*result.Value().NetworkProfile.NetworkInterfaces)[0].ID],
- instanceType: az.arvconfig.InstanceTypes[(*result.Value().Tags["arvados-instance-type"])]})
+ provider: az,
+ vm: result.Value(),
+ nic: interfaces[*(*result.Value().NetworkProfile.NetworkInterfaces)[0].ID]})
}
}
return instances, nil
@@ -522,19 +516,21 @@ func (az *AzureProvider) ManageBlobs(ctx context.Context) {
}
}
+func (az *AzureProvider) Stop() {
+}
+
type AzureInstance struct {
- instanceType arvados.InstanceType
- provider *AzureProvider
- nic network.Interface
- vm compute.VirtualMachine
+ provider *AzureProvider
+ nic network.Interface
+ vm compute.VirtualMachine
}
-func (ai *AzureInstance) String() string {
- return *ai.vm.Name
+func (ai *AzureInstance) ID() InstanceID {
+ return InstanceID(*ai.vm.ID)
}
-func (ai *AzureInstance) InstanceType() arvados.InstanceType {
- return ai.instanceType
+func (ai *AzureInstance) String() string {
+ return *ai.vm.Name
}
func (ai *AzureInstance) SetTags(ctx context.Context, newTags InstanceTags) error {
@@ -562,7 +558,7 @@ func (ai *AzureInstance) SetTags(ctx context.Context, newTags InstanceTags) erro
return nil
}
-func (ai *AzureInstance) GetTags(ctx context.Context) (InstanceTags, error) {
+func (ai *AzureInstance) Tags(ctx context.Context) (InstanceTags, error) {
tags := make(map[string]string)
for k, v := range ai.vm.Tags {
diff --git a/lib/dispatchcloud/azure_test.go b/lib/dispatchcloud/azure_test.go
index a5a173bee..c23360b32 100644
--- a/lib/dispatchcloud/azure_test.go
+++ b/lib/dispatchcloud/azure_test.go
@@ -6,9 +6,14 @@ package dispatchcloud
import (
"context"
+ "errors"
"flag"
+ "io/ioutil"
"log"
+ "net"
"net/http"
+ "os"
+ "time"
"git.curoverse.com/arvados.git/sdk/go/arvados"
"git.curoverse.com/arvados.git/sdk/go/config"
@@ -17,6 +22,7 @@ import (
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/azure"
"github.com/Azure/go-autorest/autorest/to"
+ "golang.org/x/crypto/ssh"
check "gopkg.in/check.v1"
)
@@ -64,7 +70,7 @@ func (*InterfacesClientStub) ListComplete(ctx context.Context, resourceGroupName
var live = flag.String("live-azure-cfg", "", "Test with real azure API, provide config file")
-func GetProvider() (Provider, ImageID, arvados.Cluster, error) {
+func GetProvider() (InstanceProvider, ImageID, arvados.Cluster, error) {
cluster := arvados.Cluster{
InstanceTypes: arvados.InstanceTypeMap(map[string]arvados.InstanceType{
"tiny": arvados.InstanceType{
@@ -83,14 +89,13 @@ func GetProvider() (Provider, ImageID, arvados.Cluster, error) {
if err != nil {
return nil, ImageID(""), cluster, err
}
- ap, err := NewAzureProvider(cfg, cluster, "test123")
+ ap, err := NewAzureProvider(cfg, "test123")
return ap, ImageID(cfg.Image), cluster, err
} else {
ap := AzureProvider{
azconfig: AzureProviderConfig{
BlobContainer: "vhds",
},
- arvconfig: cluster,
dispatcherID: "test123",
namePrefix: "compute-test123-",
}
@@ -106,13 +111,24 @@ func (*AzureProviderSuite) TestCreate(c *check.C) {
c.Fatal("Error making provider", err)
}
+ f, err := os.Open("azconfig_sshkey.pub")
+ c.Assert(err, check.IsNil)
+
+ keybytes, err := ioutil.ReadAll(f)
+ c.Assert(err, check.IsNil)
+
+ pk, _, _, _, err := ssh.ParseAuthorizedKey(keybytes)
+ c.Assert(err, check.IsNil)
+
inst, err := ap.Create(context.Background(),
cluster.InstanceTypes["tiny"],
- img, map[string]string{"tag1": "bleep"})
+ img, map[string]string{"tag1": "bleep"},
+ pk)
c.Assert(err, check.IsNil)
log.Printf("Result %v %v", inst.String(), inst.Address())
+
}
func (*AzureProviderSuite) TestListInstances(c *check.C) {
@@ -126,8 +142,8 @@ func (*AzureProviderSuite) TestListInstances(c *check.C) {
c.Assert(err, check.IsNil)
for _, i := range l {
- tg, _ := i.GetTags(context.Background())
- log.Printf("%v %v %v %v", i.String(), i.Address(), i.InstanceType(), tg)
+ tg, _ := i.Tags(context.Background())
+ log.Printf("%v %v %v", i.String(), i.Address(), tg)
}
}
@@ -230,7 +246,75 @@ func (*AzureProviderSuite) TestSetTags(c *check.C) {
c.Assert(err, check.IsNil)
if len(l) > 0 {
- tg, _ := l[0].GetTags(context.Background())
+ tg, _ := l[0].Tags(context.Background())
log.Printf("tags are %v", tg)
}
}
+
+func (*AzureProviderSuite) TestSSH(c *check.C) {
+ ap, _, _, err := GetProvider()
+ if err != nil {
+ c.Fatal("Error making provider", err)
+ }
+ l, err := ap.Instances(context.Background())
+ c.Assert(err, check.IsNil)
+
+ if len(l) > 0 {
+
+ sshclient, err := SetupSSHClient(c, l[0].Address()+":2222")
+ c.Assert(err, check.IsNil)
+
+ sess, err := sshclient.NewSession()
+ c.Assert(err, check.IsNil)
+
+ out, err := sess.Output("ls /")
+ c.Assert(err, check.IsNil)
+
+ log.Printf("%v", out)
+
+ sshclient.Conn.Close()
+ }
+}
+
+func SetupSSHClient(c *check.C, addr string) (*ssh.Client, error) {
+ if addr == "" {
+ return nil, errors.New("instance has no address")
+ }
+
+ f, err := os.Open("azconfig_sshkey")
+ c.Assert(err, check.IsNil)
+
+ keybytes, err := ioutil.ReadAll(f)
+ c.Assert(err, check.IsNil)
+
+ priv, err := ssh.ParsePrivateKey(keybytes)
+ c.Assert(err, check.IsNil)
+
+ var receivedKey ssh.PublicKey
+ client, err := ssh.Dial("tcp", addr, &ssh.ClientConfig{
+ User: "crunch",
+ Auth: []ssh.AuthMethod{
+ ssh.PublicKeys(priv),
+ },
+ HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
+ receivedKey = key
+ return nil
+ },
+ Timeout: time.Minute,
+ })
+
+ if err != nil {
+ return nil, err
+ } else if receivedKey == nil {
+ return nil, errors.New("BUG: key was never provided to HostKeyCallback")
+ }
+
+ /*if wkr.publicKey == nil || !bytes.Equal(wkr.publicKey.Marshal(), receivedKey.Marshal()) {
+ err = wkr.instance.VerifyPublicKey(receivedKey, client)
+ if err != nil {
+ return nil, err
+ }
+ wkr.publicKey = receivedKey
+ }*/
+ return client, nil
+}
diff --git a/lib/dispatchcloud/provider.go b/lib/dispatchcloud/provider.go
index 3322575dc..ed5eb8fe2 100644
--- a/lib/dispatchcloud/provider.go
+++ b/lib/dispatchcloud/provider.go
@@ -9,6 +9,7 @@ import (
"time"
"git.curoverse.com/arvados.git/sdk/go/arvados"
+ "golang.org/x/crypto/ssh"
)
// A RateLimitError should be returned by a Provider when the cloud
@@ -38,21 +39,44 @@ type ImageID string
// instance is implemented by the provider-specific instance types.
type Instance interface {
+ // ID returns the provider's instance ID. It must be stable
+ // for the life of the instance.
+ ID() InstanceID
+
// String typically returns the cloud-provided instance ID.
String() string
- // Configured Arvados instance type
- InstanceType() arvados.InstanceType
+
// Get tags
- GetTags(context.Context) (InstanceTags, error)
+ Tags(context.Context) (InstanceTags, error)
+
// Replace tags with the given tags
SetTags(context.Context, InstanceTags) error
+
// Shut down the node
Destroy(context.Context) error
+
// SSH server hostname or IP address, or empty string if unknown pending creation.
Address() string
}
-type Provider interface {
- Create(context.Context, arvados.InstanceType, ImageID, InstanceTags) (Instance, error)
+type InstanceProvider interface {
+ // Create a new instance. If supported by the driver, add the
+ // provided public key to /root/.ssh/authorized_keys.
+ //
+ // The returned error should implement RateLimitError and
+ // QuotaError where applicable.
+ Create(context.Context, arvados.InstanceType, ImageID, InstanceTags, ssh.PublicKey) (Instance, error)
+
+ // Return all instances, including ones that are booting or
+ // shutting down.
+ //
+ // An instance returned by successive calls to Instances() may
+ // -- but does not need to -- be represented by the same
+ // Instance object each time. Thus, the caller is responsible
+ // for de-duplicating the returned instances by comparing the
+ // InstanceIDs returned by the instances' ID() methods.
Instances(context.Context) ([]Instance, error)
+
+ // Stop any background tasks and release other resources.
+ Stop()
}
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list