Skip to content

Commit

Permalink
Merge 6fc776f into f9607be
Browse files Browse the repository at this point in the history
  • Loading branch information
richardcase authored Feb 17, 2022
2 parents f9607be + 6fc776f commit 29fc4f2
Show file tree
Hide file tree
Showing 14 changed files with 298 additions and 19 deletions.
10 changes: 10 additions & 0 deletions client/cloudinit/cloudinit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package cloudinit

const (
// InstanceDataKey is the metdata key name to use for instance data.
InstanceDataKey = "meta-data"
// UserdataKey is the metadata key name to use for user data.
UserdataKey = "user-data"
// VendorDataKey is the metadata key name to use for vendor data.
VendorDataKey = "vendor-data"
)
19 changes: 19 additions & 0 deletions client/cloudinit/instance/const.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package instance

// These constants represent standard instance metadata key names.
const (
// CloudNameKey is the instance metdata key name representing the cloud name.
CloudNameKey = "cloud_name"
// InstanceIDKey is the instance metdata key name representing the unique instance id mof the instance.
InstanceIDKey = "instance_id"
// LocalHostnameKey is the instance metdata key name representing the host name of the instance.
LocalHostnameKey = "local_hostname"
// PlatformKey is the instance metdata key name representing the hosting platform of the instance.
PlatformKey = "platform"
)

// These constants represents custom instance metadata names
const (
// ClusterNameKey is the instance metdata key name representing the cluster name of the instance.
ClusterNameKey = "cluster_name"
)
73 changes: 73 additions & 0 deletions client/cloudinit/instance/metadata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package instance

// New creates a new instance metadata
func New(opts ...MetadataOption) Metadata {
m := map[string]string{}

for _, opt := range opts {
opt(m)
}

return m
}

// Metadata represents the cloud-init instance metadata.
// See https://cloudinit.readthedocs.io/en/latest/topics/instancedata.html
type Metadata map[string]string

// HasItem returns true/false if the specific metadata item exists.
func (m Metadata) HasItem(name string) bool {
if len(m) == 0 {
return false
}
_, ok := m[name]

return ok
}

// MetadataOption is an option when creating an instance of Metadata
type MetadataOption func(Metadata)

// WithInstanceID will set the instance id metadata.
func WithInstanceID(instanceID string) MetadataOption {
return func(im Metadata) {
im[InstanceIDKey] = instanceID
}
}

// WithCloudName will set the cloud name metadata.
func WithCloudName(name string) MetadataOption {
return func(im Metadata) {
im[CloudNameKey] = name
}
}

// WithLocalHostname will set the local hostname metadata.
func WithLocalHostname(name string) MetadataOption {
return func(im Metadata) {
im[LocalHostnameKey] = name
}
}

// WithPlatform will set the platform metadata.
func WithPlatform(name string) MetadataOption {
return func(im Metadata) {
im[PlatformKey] = name
}
}

// WithClusterName will set the cluster name metadata.
func WithClusterName(name string) MetadataOption {
return func(im Metadata) {
im[ClusterNameKey] = name
}
}

// WithExisting will set the metadata keys/values based on an existing Metadata.
func WithExisting(existing Metadata) MetadataOption {
return func(im Metadata) {
for k, v := range existing {
im[k] = v
}
}
}
71 changes: 71 additions & 0 deletions client/cloudinit/instance/metadata_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package instance_test

import (
"testing"

. "github.com/onsi/gomega"

"github.com/weaveworks/flintlock/client/cloudinit/instance"
)

const (
testCloudName = "nocloud"
testClusterName = "cluster1"
testInstanceID = "i-123456"
testHostName = "host1"
testPlatformName = "liquidmetal"
)

func TestInstanceMetadata_NewNoOptions(t *testing.T) {
RegisterTestingT(t)

m := instance.New()
Expect(m).NotTo(BeNil())
Expect(m).To(HaveLen(0))
}

func TestInstanceMetadata_NewWithOptions(t *testing.T) {
RegisterTestingT(t)

m := instance.New(
instance.WithCloudName(testCloudName),
instance.WithClusterName(testClusterName),
instance.WithInstanceID(testInstanceID),
instance.WithLocalHostname(testHostName),
instance.WithPlatform(testPlatformName),
)
Expect(m).NotTo(BeNil())
Expect(m).To(HaveLen(5))

Expect(m[instance.CloudNameKey]).To(Equal(testCloudName))
Expect(m[instance.ClusterNameKey]).To(Equal(testClusterName))
Expect(m[instance.InstanceIDKey]).To(Equal(testInstanceID))
Expect(m[instance.LocalHostnameKey]).To(Equal(testHostName))
Expect(m[instance.PlatformKey]).To(Equal(testPlatformName))
}

func TestInstanceMetadata_NewOptionsExisting(t *testing.T) {
RegisterTestingT(t)

existing := instance.New(
instance.WithCloudName(testCloudName),
instance.WithClusterName(testClusterName),
instance.WithInstanceID(testInstanceID),
instance.WithLocalHostname(testHostName),
instance.WithPlatform(testPlatformName),
)

m := instance.New(
instance.WithExisting(existing),
instance.WithInstanceID("changed"),
)

Expect(m).NotTo(BeNil())
Expect(m).To(HaveLen(5))

Expect(m[instance.CloudNameKey]).To(Equal(testCloudName))
Expect(m[instance.ClusterNameKey]).To(Equal(testClusterName))
Expect(m[instance.InstanceIDKey]).To(Equal("changed"))
Expect(m[instance.LocalHostnameKey]).To(Equal(testHostName))
Expect(m[instance.PlatformKey]).To(Equal(testPlatformName))
}
8 changes: 0 additions & 8 deletions client/cloudinit/metadata.go

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package cloudinit
package network

type Network struct {
Version int `yaml:"version"`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package cloudinit
package userdata

type UserData struct {
HostName string `yaml:"hostname,omitempty"`
Expand Down
1 change: 1 addition & 0 deletions client/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ require (
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect
google.golang.org/grpc v1.44.0 // indirect
google.golang.org/protobuf v1.25.0 // indirect
github.com/onsi/gomega v1.18.1 // indirect
)
64 changes: 62 additions & 2 deletions core/application/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@ package application_test

import (
"context"
"encoding/base64"
"errors"
"testing"
"time"

"github.com/golang/mock/gomock"
. "github.com/onsi/gomega"
"github.com/spf13/afero"
"sigs.k8s.io/yaml"

"github.com/weaveworks/flintlock/api/events"
"github.com/weaveworks/flintlock/client/cloudinit/instance"
"github.com/weaveworks/flintlock/core/application"
"github.com/weaveworks/flintlock/core/models"
"github.com/weaveworks/flintlock/core/ports"
Expand Down Expand Up @@ -55,7 +58,7 @@ func TestApp_CreateMicroVM(t *testing.T) {
}),
).Return(nil, nil)

expectedCreatedSpec := createTestSpec("id1234", defaults.MicroVMNamespace, testUID)
expectedCreatedSpec := createTestSpecWithMetadata("id1234", defaults.MicroVMNamespace, testUID, createInstanceMetadatadata(testUID))
expectedCreatedSpec.Spec.CreatedAt = frozenTime().Unix()
expectedCreatedSpec.Status.State = models.PendingState

Expand Down Expand Up @@ -96,7 +99,7 @@ func TestApp_CreateMicroVM(t *testing.T) {
nil,
)

expectedCreatedSpec := createTestSpec("id1234", "default", testUID)
expectedCreatedSpec := createTestSpecWithMetadata("id1234", "default", testUID, createInstanceMetadatadata(testUID))
expectedCreatedSpec.Spec.CreatedAt = frozenTime().Unix()
expectedCreatedSpec.Status.State = models.PendingState

Expand Down Expand Up @@ -138,6 +141,47 @@ func TestApp_CreateMicroVM(t *testing.T) {
)
},
},
{
name: "spec with id, namespace and existing instance data create",
specToCreate: createTestSpecWithMetadata("id1234", "default", testUID, createInstanceMetadatadata("abcdef")),
expectError: false,
expect: func(rm *mock.MockMicroVMRepositoryMockRecorder, em *mock.MockEventServiceMockRecorder, im *mock.MockIDServiceMockRecorder, pm *mock.MockMicroVMServiceMockRecorder) {
im.GenerateRandom().Return(testUID, nil).Times(1)
rm.Get(
gomock.AssignableToTypeOf(context.Background()),
gomock.Eq(ports.RepositoryGetOptions{
Name: "id1234",
Namespace: "default",
UID: testUID,
}),
).Return(
nil,
nil,
)

expectedCreatedSpec := createTestSpecWithMetadata("id1234", "default", testUID, createInstanceMetadatadata("abcdef"))
expectedCreatedSpec.Spec.CreatedAt = frozenTime().Unix()
expectedCreatedSpec.Status.State = models.PendingState

rm.Save(
gomock.AssignableToTypeOf(context.Background()),
gomock.Eq(expectedCreatedSpec),
).Return(
createTestSpecWithMetadata("id1234", "default", testUID, createInstanceMetadatadata("abcdef")),
nil,
)

em.Publish(
gomock.AssignableToTypeOf(context.Background()),
gomock.Eq(defaults.TopicMicroVMEvents),
gomock.Eq(&events.MicroVMSpecCreated{
ID: "id1234",
Namespace: "default",
UID: testUID,
}),
)
},
},
}

for _, tc := range testCases {
Expand Down Expand Up @@ -556,6 +600,10 @@ func TestApp_GetAllMicroVM(t *testing.T) {
}

func createTestSpec(name, ns, uid string) *models.MicroVM {
return createTestSpecWithMetadata(name, ns, uid, map[string]string{})
}

func createTestSpecWithMetadata(name, ns, uid string, metadata map[string]string) *models.MicroVM {
var vmid *models.VMID

if uid == "" {
Expand Down Expand Up @@ -600,9 +648,21 @@ func createTestSpec(name, ns, uid string) *models.MicroVM {
},
Size: 20000,
},
Metadata: metadata,
CreatedAt: 0,
UpdatedAt: 0,
DeletedAt: 0,
},
}
}

func createInstanceMetadatadata(instanceID string) map[string]string {
instanceData := instance.New(instance.WithInstanceID(instanceID))
data, _ := yaml.Marshal(instanceData)

instanceDataStr := base64.StdEncoding.EncodeToString(data)

return map[string]string{
"meta-data": instanceDataStr,
}
}
50 changes: 50 additions & 0 deletions core/application/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@ package application

import (
"context"
"encoding/base64"
"fmt"

"github.com/sirupsen/logrus"
"sigs.k8s.io/yaml"

"github.com/weaveworks/flintlock/api/events"
"github.com/weaveworks/flintlock/client/cloudinit"
"github.com/weaveworks/flintlock/client/cloudinit/instance"
coreerrs "github.com/weaveworks/flintlock/core/errors"
"github.com/weaveworks/flintlock/core/models"
"github.com/weaveworks/flintlock/core/ports"
Expand Down Expand Up @@ -40,6 +46,7 @@ func (a *app) CreateMicroVM(ctx context.Context, mvm *models.MicroVM) (*models.M
}

mvm.ID.SetUID(uid)
logger = logger.WithField("vmid", mvm.ID)

foundMvm, err := a.ports.Repo.Get(ctx, ports.RepositoryGetOptions{
Name: mvm.ID.Name(),
Expand All @@ -60,6 +67,11 @@ func (a *app) CreateMicroVM(ctx context.Context, mvm *models.MicroVM) (*models.M
}
}

err = a.addInstanceData(mvm, logger)
if err != nil {
return nil, fmt.Errorf("adding instance data: %w", err)
}

// Set the timestamp when the VMspec was created.
mvm.Spec.CreatedAt = a.ports.Clock().Unix()
mvm.Status.State = models.PendingState
Expand Down Expand Up @@ -122,3 +134,41 @@ func (a *app) DeleteMicroVM(ctx context.Context, uid string) error {

return nil
}

func (a *app) addInstanceData(vm *models.MicroVM, logger *logrus.Entry) error {
instanceData := instance.New()

meta := vm.Spec.Metadata[cloudinit.InstanceDataKey]
if meta != "" {
logger.Info("Instance metadata exists")

data, err := base64.StdEncoding.DecodeString(meta)
if err != nil {
return fmt.Errorf("decoding existing instance metadata: %w", err)
}

err = yaml.Unmarshal(data, &instanceData)
if err != nil {
return fmt.Errorf("unmarshalling exists instance metadata: %w", err)
}
}

existingInstanceID := instanceData[instance.InstanceIDKey]
if existingInstanceID != "" {
logger.Infof("Instance id already set in meta-data: %s", existingInstanceID)

return nil
}

logger.Infof("Setting instance_id in meta-data: %s", vm.ID.UID())
instanceData[instance.InstanceIDKey] = vm.ID.UID()

updatedData, err := yaml.Marshal(&instanceData)
if err != nil {
return fmt.Errorf("marshalling updated instance data: %w", err)
}

vm.Spec.Metadata[cloudinit.InstanceDataKey] = base64.StdEncoding.EncodeToString(updatedData)

return nil
}
Loading

0 comments on commit 29fc4f2

Please sign in to comment.