Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set instance_id in flintlock #394

Merged
merged 1 commit into from
Feb 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"
)
80 changes: 80 additions & 0 deletions client/cloudinit/instance/metadata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
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
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a custom WithKeyVaue or WithCustomField would be useful. Right now we are not using anything else, but in production ppl might want to define more data.

func WithKeyValue(key, value string) MetadataOption {
	return func(im Metadata) {
		im[key] = value
	}
}

potentially all WithXXX can use that as well, so if we need validation we can do it in one place (like don't add if the value is empty)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thats a good point. Let me change that.


// WithKeyValue will set the metadata with the specified key and value.
func WithKeyValue(key, value string) MetadataOption {
return func(im Metadata) {
im[key] = value
}
}
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))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could simplify this to just HaveLen(5) because it can't be both nil and have len 5

The one above is fine (NotTo(BeNil())) because you want to ensure that it's a len 0 slice, and nil has len 0.

(There is another below).


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
)
67 changes: 65 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(t, 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(t, 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(t, "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(t, "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(t, "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,24 @@ func createTestSpec(name, ns, uid string) *models.MicroVM {
},
Size: 20000,
},
Metadata: metadata,
CreatedAt: 0,
UpdatedAt: 0,
DeletedAt: 0,
},
}
}

func createInstanceMetadatadata(t *testing.T, instanceID string) map[string]string {
RegisterTestingT(t)

instanceData := instance.New(instance.WithInstanceID(instanceID))
data, err := yaml.Marshal(instanceData)
Expect(err).NotTo(HaveOccurred())

instanceDataStr := base64.StdEncoding.EncodeToString(data)

return map[string]string{
"meta-data": instanceDataStr,
}
}
Loading