Skip to content

Commit bf79563

Browse files
authored
Merge af74aac into bfd2b3f
2 parents bfd2b3f + af74aac commit bf79563

File tree

43 files changed

+1327
-404
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1327
-404
lines changed

README.md

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,21 @@
77

88
## What is flintlock?
99

10-
Flintlock is a service for creating and managing the lifecycle of microVMs on a host machine. Initially we will be supporting [Firecracker](https://firecracker-microvm.github.io/).
10+
Flintlock is a service for creating and managing the lifecycle of microVMs on a host machine. We support [Firecracker](https://firecracker-microvm.github.io/) and [Cloud Hypervisor](https://www.cloudhypervisor.org/) (experimental).
1111

12-
The primary use case for flintlock is to create microVMs on a bare-metal host where the microVMs will be used as nodes in a virtualized Kubernetes cluster. It is an essential part of [Liquid Metal](https://www.weave.works/blog/multi-cluster-kubernetes-on-microvms-for-bare-metal) and will ultimately be driven by Cluster API Provider Microvm (coming soon).
12+
The original use case for flintlock was to create microVMs on a bare-metal host where the microVMs will be used as nodes in a virtualized Kubernetes cluster. It is an essential part of [Liquid Metal](https://www.weave.works/blog/multi-cluster-kubernetes-on-microvms-for-bare-metal) and can be orchestrated by [Cluster API Provider Microvm](https://github.com/weaveworks-liquidmetal/cluster-api-provider-microvm).
13+
14+
However, its useful for many other use cases where lightweight virtualization is required (e.g. isolated workloads, pipelines).
1315

1416
## Features
1517

1618
Using API requests (via gRPC or HTTP):
1719

18-
- Create and delete microVMs using Firecracker
20+
- Create and delete microVMs
1921
- Manage the lifecycle of microVMs (i.e. start, stop, pause)
2022
- Configure microVM metadata via cloud-init, ignition etc
2123
- Use OCI images for microVM volumes, kernel and initrd
24+
- Expose microVM metrics for collection by Prometheus
2225
- (coming soon) Use CNI to configure the network for the microVMs
2326

2427
## Documentation
@@ -50,13 +53,15 @@ Your feedback is always welcome!
5053

5154
The table below shows you which versions of Firecracker are compatible with Flintlock:
5255

53-
| Flintlock | Firecracker |
54-
| ----------------- | ---------------------------------- |
55-
| >= v0.3.0 | Official v1.0+ or >=v1.0.0-macvtap |
56-
| <= v0.2.0 | <= v0.25.2-macvtap |
57-
| <= v0.1.0-alpha.6 | <= v0.25.2-macvtap |
58-
| v0.1.0-alpha.7 | **Do not use** |
59-
| v0.1.0-alpha.8 | <= v0.25.2-macvtap |
56+
| Flintlock | Firecracker | Cloud Hypervisor |
57+
| ----------------- | -------------------------------- | ----------------- |
58+
| v0.5.0 | Official v1.0+ or v1.0.0-macvtap | v26.0 |
59+
| v0.4.0 | Official v1.0+ or v1.0.0-macvtap | **Not Supported** |
60+
| v0.3.0 | Official v1.0+ or v1.0.0-macvtap | **Not Supported** |
61+
| <= v0.2.0 | <= v0.25.2-macvtap | **Not Supported** |
62+
| <= v0.1.0-alpha.6 | <= v0.25.2-macvtap | **Not Supported** |
63+
| v0.1.0-alpha.7 | **Do not use** | **Not Supported** |
64+
| v0.1.0-alpha.8 | <= v0.25.2-macvtap | **Not Supported** |
6065

6166
> Note: Flintlock currently requires a custom build of Firecracker if you plan to use `macvtap` available [here][fc-fork].
6267

api/services/microvm/v1alpha1/microvms.swagger.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,10 @@
341341
"uid": {
342342
"type": "string",
343343
"description": "UID is a globally unique identifier of the microvm."
344+
},
345+
"provider": {
346+
"type": "string",
347+
"description": "Provider allows you to specify the name of the microvm provider to use. If this isn't supplied\nthen the default provider will be used."
344348
}
345349
},
346350
"description": "MicroVMSpec represents the specification for a microvm."

api/types/microvm.pb.go

Lines changed: 168 additions & 156 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/types/microvm.proto

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ message MicroVMSpec {
6666
// UID is a globally unique identifier of the microvm.
6767
optional string uid = 15;
6868

69+
// Provider allows you to specify the name of the microvm provider to use. If this isn't supplied
70+
// then the default provider will be used.
71+
optional string provider = 16;
6972
}
7073

7174
// Kernel represents the configuration for a kernel.

client/cloudinit/cloudinit.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,8 @@ const (
77
UserdataKey = "user-data"
88
// VendorDataKey is the metadata key name to use for vendor data.
99
VendorDataKey = "vendor-data"
10+
// NetworkConfigDataKey is the metadata key name for the network config.
11+
NetworkConfigDataKey = "network-config"
12+
// VolumeName is the name of a volume that contains cloud-init data.
13+
VolumeName = "cidata"
1014
)

core/application/app.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ type app struct {
2525
}
2626

2727
type Config struct {
28-
RootStateDir string
29-
MaximumRetry int
28+
RootStateDir string
29+
MaximumRetry int
30+
DefaultProvider string
3031
}

core/application/app_test.go

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ func TestApp_CreateMicroVM(t *testing.T) {
4747
expectError: false,
4848
expect: func(rm *mock.MockMicroVMRepositoryMockRecorder, em *mock.MockEventServiceMockRecorder, im *mock.MockIDServiceMockRecorder, pm *mock.MockMicroVMServiceMockRecorder) {
4949
im.GenerateRandom().Return("id1234", nil).Times(1)
50+
51+
pm.Capabilities().Return(models.Capabilities{models.MetadataServiceCapability})
52+
5053
im.GenerateRandom().Return(testUID, nil).Times(1)
5154

5255
rm.Get(
@@ -59,6 +62,7 @@ func TestApp_CreateMicroVM(t *testing.T) {
5962
).Return(nil, nil)
6063

6164
expectedCreatedSpec := createTestSpecWithMetadata("id1234", defaults.MicroVMNamespace, testUID, createInstanceMetadatadata(t, testUID))
65+
expectedCreatedSpec.Spec.Provider = "mock"
6266
expectedCreatedSpec.Spec.CreatedAt = frozenTime().Unix()
6367
expectedCreatedSpec.Status.State = models.PendingState
6468

@@ -86,6 +90,7 @@ func TestApp_CreateMicroVM(t *testing.T) {
8690
specToCreate: createTestSpec("id1234", "default", testUID),
8791
expectError: false,
8892
expect: func(rm *mock.MockMicroVMRepositoryMockRecorder, em *mock.MockEventServiceMockRecorder, im *mock.MockIDServiceMockRecorder, pm *mock.MockMicroVMServiceMockRecorder) {
93+
pm.Capabilities().Return(models.Capabilities{models.MetadataServiceCapability})
8994
im.GenerateRandom().Return(testUID, nil).Times(1)
9095
rm.Get(
9196
gomock.AssignableToTypeOf(context.Background()),
@@ -100,6 +105,7 @@ func TestApp_CreateMicroVM(t *testing.T) {
100105
)
101106

102107
expectedCreatedSpec := createTestSpecWithMetadata("id1234", "default", testUID, createInstanceMetadatadata(t, testUID))
108+
expectedCreatedSpec.Spec.Provider = "mock"
103109
expectedCreatedSpec.Spec.CreatedAt = frozenTime().Unix()
104110
expectedCreatedSpec.Status.State = models.PendingState
105111

@@ -146,6 +152,7 @@ func TestApp_CreateMicroVM(t *testing.T) {
146152
specToCreate: createTestSpecWithMetadata("id1234", "default", testUID, createInstanceMetadatadata(t, "abcdef")),
147153
expectError: false,
148154
expect: func(rm *mock.MockMicroVMRepositoryMockRecorder, em *mock.MockEventServiceMockRecorder, im *mock.MockIDServiceMockRecorder, pm *mock.MockMicroVMServiceMockRecorder) {
155+
pm.Capabilities().Return(models.Capabilities{models.MetadataServiceCapability})
149156
im.GenerateRandom().Return(testUID, nil).Times(1)
150157
rm.Get(
151158
gomock.AssignableToTypeOf(context.Background()),
@@ -160,6 +167,7 @@ func TestApp_CreateMicroVM(t *testing.T) {
160167
)
161168

162169
expectedCreatedSpec := createTestSpecWithMetadata("id1234", "default", testUID, createInstanceMetadatadata(t, "abcdef"))
170+
expectedCreatedSpec.Spec.Provider = "mock"
163171
expectedCreatedSpec.Spec.CreatedAt = frozenTime().Unix()
164172
expectedCreatedSpec.Status.State = models.PendingState
165173

@@ -199,8 +207,10 @@ func TestApp_CreateMicroVM(t *testing.T) {
199207
is := mock.NewMockImageService(mockCtrl)
200208
fs := afero.NewMemMapFs()
201209
ports := &ports.Collection{
202-
Repo: rm,
203-
Provider: pm,
210+
Repo: rm,
211+
MicrovmProviders: map[string]ports.MicroVMService{
212+
"mock": pm,
213+
},
204214
EventService: em,
205215
IdentifierService: im,
206216
NetworkService: ns,
@@ -212,7 +222,7 @@ func TestApp_CreateMicroVM(t *testing.T) {
212222
tc.expect(rm.EXPECT(), em.EXPECT(), im.EXPECT(), pm.EXPECT())
213223

214224
ctx := context.Background()
215-
app := application.New(&application.Config{}, ports)
225+
app := application.New(&application.Config{DefaultProvider: "mock"}, ports)
216226
_, err := app.CreateMicroVM(ctx, tc.specToCreate)
217227

218228
if tc.expectError {
@@ -311,8 +321,10 @@ func TestApp_DeleteMicroVM(t *testing.T) {
311321
is := mock.NewMockImageService(mockCtrl)
312322
fs := afero.NewMemMapFs()
313323
ports := &ports.Collection{
314-
Repo: rm,
315-
Provider: pm,
324+
Repo: rm,
325+
MicrovmProviders: map[string]ports.MicroVMService{
326+
"mock": pm,
327+
},
316328
EventService: em,
317329
IdentifierService: im,
318330
NetworkService: ns,
@@ -324,7 +336,7 @@ func TestApp_DeleteMicroVM(t *testing.T) {
324336
tc.expect(rm.EXPECT(), em.EXPECT(), im.EXPECT(), pm.EXPECT())
325337

326338
ctx := context.Background()
327-
app := application.New(&application.Config{}, ports)
339+
app := application.New(&application.Config{DefaultProvider: "mock"}, ports)
328340
err := app.DeleteMicroVM(ctx, tc.toDeleteUID)
329341

330342
if tc.expectError {
@@ -417,8 +429,10 @@ func TestApp_GetMicroVM(t *testing.T) {
417429
is := mock.NewMockImageService(mockCtrl)
418430
fs := afero.NewMemMapFs()
419431
ports := &ports.Collection{
420-
Repo: rm,
421-
Provider: pm,
432+
Repo: rm,
433+
MicrovmProviders: map[string]ports.MicroVMService{
434+
"mock": pm,
435+
},
422436
EventService: em,
423437
IdentifierService: im,
424438
NetworkService: ns,
@@ -430,7 +444,7 @@ func TestApp_GetMicroVM(t *testing.T) {
430444
tc.expect(rm.EXPECT(), em.EXPECT(), im.EXPECT(), pm.EXPECT())
431445

432446
ctx := context.Background()
433-
app := application.New(&application.Config{}, ports)
447+
app := application.New(&application.Config{DefaultProvider: "mock"}, ports)
434448
mvm, err := app.GetMicroVM(ctx, tc.toGetUID)
435449

436450
if tc.expectError {
@@ -560,8 +574,10 @@ func TestApp_GetAllMicroVM(t *testing.T) {
560574
is := mock.NewMockImageService(mockCtrl)
561575
fs := afero.NewMemMapFs()
562576
ports := &ports.Collection{
563-
Repo: rm,
564-
Provider: pm,
577+
Repo: rm,
578+
MicrovmProviders: map[string]ports.MicroVMService{
579+
"mock": pm,
580+
},
565581
EventService: em,
566582
IdentifierService: im,
567583
NetworkService: ns,
@@ -573,7 +589,7 @@ func TestApp_GetAllMicroVM(t *testing.T) {
573589
tc.expect(rm.EXPECT(), em.EXPECT(), im.EXPECT(), pm.EXPECT())
574590

575591
ctx := context.Background()
576-
app := application.New(&application.Config{}, ports)
592+
app := application.New(&application.Config{DefaultProvider: "mock"}, ports)
577593
query := models.ListMicroVMQuery{"namespace": tc.toGetNS}
578594

579595
if tc.toGetName != nil {

core/application/commands.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,15 @@ func (a *app) CreateMicroVM(ctx context.Context, mvm *models.MicroVM) (*models.M
5050
mvm.ID = *vmid
5151
}
5252

53+
if mvm.Spec.Provider == "" {
54+
mvm.Spec.Provider = a.cfg.DefaultProvider
55+
}
56+
provider, ok := a.ports.MicrovmProviders[mvm.Spec.Provider]
57+
if !ok {
58+
return nil, fmt.Errorf("microvm provider %s isn't available", mvm.Spec.Provider)
59+
}
60+
logger = logger.WithField("microvm-provider", mvm.Spec.Provider)
61+
5362
uid, err := a.ports.IdentifierService.GenerateRandom()
5463
if err != nil {
5564
return nil, fmt.Errorf("generating random ID for microvm: %w", err)
@@ -81,7 +90,9 @@ func (a *app) CreateMicroVM(ctx context.Context, mvm *models.MicroVM) (*models.M
8190
if err != nil {
8291
return nil, fmt.Errorf("adding instance data: %w", err)
8392
}
84-
a.addMetadataInterface(mvm)
93+
if provider.Capabilities().Has(models.MetadataServiceCapability) {
94+
a.addMetadataInterface(mvm)
95+
}
8596

8697
// Set the timestamp when the VMspec was created.
8798
mvm.Spec.CreatedAt = a.ports.Clock().Unix()

core/errors/errors.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,25 @@ func IsSpecNotFound(err error) bool {
123123

124124
return errors.As(err, e)
125125
}
126+
127+
func NewNotSupported(featureName string) error {
128+
return notSupportedError{
129+
unsupported: featureName,
130+
}
131+
}
132+
133+
type notSupportedError struct {
134+
unsupported string
135+
}
136+
137+
// Error returns the error message.
138+
func (e notSupportedError) Error() string {
139+
return fmt.Sprintf("%s is not supported", e.unsupported)
140+
}
141+
142+
// IsNotSupported tests an error to see if its a not supported error.
143+
func IsNotSupported(err error) bool {
144+
e := &notSupportedError{}
145+
146+
return errors.As(err, e)
147+
}

core/models/capability.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@ type Capability string
66
const (
77
// MetadataServiceCapability is a capability that indicates the microvm provider
88
// has a metadata service.
9-
MetadataServiceCapability = "metadata-service"
9+
MetadataServiceCapability Capability = "metadata-service"
10+
11+
// AutoStartCapability is a capability of the microvm provider where the vm is automatically started
12+
// as part of the creation process. If a provider doesn't have this capability then its assumed the
13+
// microvm will be started via a call to the start implementation of the provider.
14+
AutoStartCapability Capability = "auto-start"
1015
)
1116

1217
// Capabilities represents a list of capabilities.

0 commit comments

Comments
 (0)