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

feat(hatchery/openstack): check flavor #5148

Merged
merged 5 commits into from
Apr 28, 2020
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
2 changes: 1 addition & 1 deletion engine/hatchery/openstack/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ func (h *HatcheryOpenstack) getServers(ctx context.Context) []servers.Server {
if !worker {
continue
}
workerHatcheryName, _ := s.Metadata["hatchery_name"]
workerHatcheryName := s.Metadata["hatchery_name"]
if workerHatcheryName == "" || workerHatcheryName != h.Name() {
continue
}
Expand Down
27 changes: 14 additions & 13 deletions engine/hatchery/openstack/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/tenantnetworks"
"github.com/gophercloud/gophercloud/openstack/compute/v2/flavors"

"github.com/ovh/cds/sdk"
"github.com/ovh/cds/sdk/log"
)

Expand All @@ -27,27 +28,27 @@ func (h *HatcheryOpenstack) InitHatchery(ctx context.Context) error {
DomainName: h.Config.Domain,
}

provider, errac := openstack.AuthenticatedClient(authOpts)
if errac != nil {
return fmt.Errorf("Unable to openstack.AuthenticatedClient: %v", errac)
provider, err := openstack.AuthenticatedClient(authOpts)
if err != nil {
return sdk.WithStack(fmt.Errorf("unable to openstack.AuthenticatedClient: %v", err))
}

openstackClient, errn := openstack.NewComputeV2(provider, gophercloud.EndpointOpts{Region: h.Config.Region})
if errn != nil {
return fmt.Errorf("Unable to openstack.NewComputeV2: %s", errn)
openstackClient, err := openstack.NewComputeV2(provider, gophercloud.EndpointOpts{Region: h.Config.Region})
if err != nil {
return sdk.WithStack(fmt.Errorf("unable to openstack.NewComputeV2: %v", err))
}
h.openstackClient = openstackClient

if err := h.initFlavors(); err != nil {
log.Warning(ctx, "Error getting flavors: %s", err)
log.Warning(ctx, "Error getting flavors: %v", err)
}

if err := h.initNetworks(); err != nil {
log.Warning(ctx, "Error getting networks: %s", err)
log.Warning(ctx, "Error getting networks: %v", err)
}

if err := h.initIPStatus(ctx); err != nil {
log.Warning(ctx, "Error on initIPStatus(): %s", err)
log.Warning(ctx, "Error on initIPStatus(): %v", err)
}

go h.main(ctx)
Expand All @@ -58,11 +59,11 @@ func (h *HatcheryOpenstack) InitHatchery(ctx context.Context) error {
func (h *HatcheryOpenstack) initFlavors() error {
all, err := flavors.ListDetail(h.openstackClient, nil).AllPages()
if err != nil {
return fmt.Errorf("initFlavors> error on flavors.ListDetail: %s", err)
return sdk.WithStack(fmt.Errorf("initFlavors> error on flavors.ListDetail: %v", err))
}
lflavors, err := flavors.ExtractFlavors(all)
if err != nil {
return fmt.Errorf("initFlavors> error on flavors.ExtractFlavors: %s", err)
return sdk.WithStack(fmt.Errorf("initFlavors> error on flavors.ExtractFlavors: %v", err))
}
h.flavors = lflavors
return nil
Expand All @@ -71,11 +72,11 @@ func (h *HatcheryOpenstack) initFlavors() error {
func (h *HatcheryOpenstack) initNetworks() error {
all, err := tenantnetworks.List(h.openstackClient).AllPages()
if err != nil {
return fmt.Errorf("initNetworks> Unable to get Network: %s", err)
return sdk.WithStack(fmt.Errorf("initNetworks> Unable to get Network: %v", err))
}
nets, err := tenantnetworks.ExtractNetworks(all)
if err != nil {
return fmt.Errorf("initNetworks> Unable to get Network: %s", err)
return sdk.WithStack(fmt.Errorf("initNetworks> Unable to get Network: %v", err))
}
for _, n := range nets {
if n.Name == h.Config.NetworkString {
Expand Down
14 changes: 11 additions & 3 deletions engine/hatchery/openstack/openstack.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,15 @@ func (h *HatcheryOpenstack) WorkerModelsEnabled() ([]sdk.Model, error) {
// CanSpawn return wether or not hatchery can spawn model
// requirements are not supported
func (h *HatcheryOpenstack) CanSpawn(ctx context.Context, model *sdk.Model, jobID int64, requirements []sdk.Requirement) bool {
// if there is a model, we have to check if the flavor attached to model is knowned by this hatchery
if model != nil {
if _, err := h.flavorID(model.ModelVirtualMachine.Flavor); err != nil {
log.Debug("CanSpawn> h.flavorID on %s err:%v", model.ModelVirtualMachine.Flavor, err)
return false
}
log.Debug("CanSpawn> flavor '%s' found", model.ModelVirtualMachine.Flavor)
}

for _, r := range requirements {
if r.Type == sdk.ServiceRequirement || r.Type == sdk.MemoryRequirement || r.Type == sdk.HostnameRequirement {
return false
Expand Down Expand Up @@ -297,7 +306,7 @@ func (h *HatcheryOpenstack) killAwolServers(ctx context.Context) {
func (h *HatcheryOpenstack) killAwolServersComputeImage(ctx context.Context, workerModelName, workerModelNameLastModified, serverID, model, flavor string) {
oldImagesID := []string{}
for _, img := range h.getImages(ctx) {
if w, _ := img.Metadata["worker_model_name"]; w == workerModelName {
if w := img.Metadata["worker_model_name"]; w == workerModelName {
oldImagesID = append(oldImagesID, img.ID)
if d, ok := img.Metadata["worker_model_last_modified"]; ok && d.(string) == workerModelNameLastModified {
// no need to recreate an image
Expand Down Expand Up @@ -460,8 +469,7 @@ func (h *HatcheryOpenstack) NeedRegistration(ctx context.Context, m *sdk.Model)
return true
}
for _, img := range h.getImages(ctx) {
w, _ := img.Metadata["worker_model_name"]
if w == m.Name {
if w := img.Metadata["worker_model_name"]; w == m.Name {
if d, ok := img.Metadata["worker_model_last_modified"]; ok {
if fmt.Sprintf("%d", m.UserLastModified.Unix()) == d.(string) {
log.Debug("NeedRegistration> false. An image is already available for this worker model %s workerModel.UserLastModified", m.Name)
Expand Down
68 changes: 68 additions & 0 deletions engine/hatchery/openstack/openstack_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package openstack

import (
"context"
"testing"

"github.com/gophercloud/gophercloud/openstack/compute/v2/flavors"
"github.com/stretchr/testify/require"

"github.com/ovh/cds/sdk"
)

func TestHatcheryOpenstack_CanSpawn(t *testing.T) {
h := &HatcheryOpenstack{}

// no model, no requirement, canSpawn must be true
canSpawn := h.CanSpawn(context.TODO(), nil, 1, nil)
require.True(t, canSpawn)

// no model, service requirement, canSpawn must be false: service can't be managed by openstack hatchery
canSpawn = h.CanSpawn(context.TODO(), nil, 1, []sdk.Requirement{{Name: "pg", Type: sdk.ServiceRequirement, Value: "postgres:9.5.4"}})
require.False(t, canSpawn)

// no model, memory prerequisite, canSpawn must be false: memory prerequisite can't be managed by openstack hatchery
canSpawn = h.CanSpawn(context.TODO(), nil, 1, []sdk.Requirement{{Name: "mem", Type: sdk.MemoryRequirement, Value: "4096"}})
require.False(t, canSpawn)

// no model, hostname prerequisite, canSpawn must be false: hostname can't be managed by openstack hatchery
canSpawn = h.CanSpawn(context.TODO(), nil, 1, []sdk.Requirement{{Type: sdk.HostnameRequirement, Value: "localhost"}})
require.False(t, canSpawn)

flavors := []flavors.Flavor{
{Name: "b2-7"},
}
h.flavors = flavors

m := &sdk.Model{
ID: 1,
Name: "my-model",
Group: &sdk.Group{
ID: 1,
Name: "mygroup",
},
ModelVirtualMachine: sdk.ModelVirtualMachine{
Flavor: "vps-ssd-3",
},
}

// model with a unknowned flavor
canSpawn = h.CanSpawn(context.TODO(), m, 1, nil)
require.False(t, canSpawn)

m = &sdk.Model{
ID: 1,
Name: "my-model",
Group: &sdk.Group{
ID: 1,
Name: "mygroup",
},
ModelVirtualMachine: sdk.ModelVirtualMachine{
Flavor: "b2-7",
},
}

// model with a knowned flavor
canSpawn = h.CanSpawn(context.TODO(), m, 1, nil)
require.True(t, canSpawn)
}
4 changes: 2 additions & 2 deletions engine/hatchery/openstack/spawn.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ func (h *HatcheryOpenstack) SpawnWorker(ctx context.Context, spawnArgs hatchery.
imgs := h.getImages(ctx)
log.Debug("spawnWorker> call images.List on openstack took %fs, nbImages:%d", time.Since(start).Seconds(), len(imgs))
for _, img := range imgs {
workerModelName, _ := img.Metadata["worker_model_name"]
workerModelLastModified, _ := img.Metadata["worker_model_last_modified"]
workerModelName := img.Metadata["worker_model_name"]
workerModelLastModified := img.Metadata["worker_model_last_modified"]
if workerModelName == spawnArgs.Model.Name && fmt.Sprintf("%s", workerModelLastModified) == fmt.Sprintf("%d", spawnArgs.Model.UserLastModified.Unix()) {
withExistingImage = true
imageID = img.ID
Expand Down
4 changes: 0 additions & 4 deletions engine/hatchery/openstack/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ import (
"github.com/ovh/cds/engine/service"

"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/tenantnetworks"
"github.com/gophercloud/gophercloud/openstack/compute/v2/flavors"
"github.com/gophercloud/gophercloud/openstack/compute/v2/images"

hatcheryCommon "github.com/ovh/cds/engine/hatchery"
)
Expand Down Expand Up @@ -57,8 +55,6 @@ type HatcheryOpenstack struct {
hatcheryCommon.Common
Config HatcheryConfiguration
flavors []flavors.Flavor
networks []tenantnetworks.Network
images []images.Image
openstackClient *gophercloud.ServiceClient

networkID string // computed from networkString
Expand Down