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

SKS-2193: Support setting the owner of ElfCluster and virtual machines #162

Merged
merged 2 commits into from
Dec 7, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
7 changes: 7 additions & 0 deletions api/v1beta1/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ const (

// CAPEVersionAnnotation is the annotation identifying the version of CAPE that the resource reconciled by.
CAPEVersionAnnotation = "cape.infrastructure.cluster.x-k8s.io/cape-version"

// CreatedByAnnotation is the annotation identifying the creator of the resource.
//
// The creator can be in one of the following two formats:
// 1. ${Tower username}@${Tower auth_config_id}, e.g. caas.smartx@7e98ecbb-779e-43f6-8330-1bc1d29fffc7.
// 2. ${Tower username}, e.g. root. If auth_config_id is not set, it means it is a LOCAL user.
CreatedByAnnotation = "cape.infrastructure.cluster.x-k8s.io/created-by"
)

// Labels.
Expand Down
7 changes: 4 additions & 3 deletions controllers/elfmachine_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
labelsutil "github.com/smartxworks/cluster-api-provider-elf/pkg/util/labels"
machineutil "github.com/smartxworks/cluster-api-provider-elf/pkg/util/machine"
patchutil "github.com/smartxworks/cluster-api-provider-elf/pkg/util/patch"
typesutil "github.com/smartxworks/cluster-api-provider-elf/pkg/util/types"
)

//+kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=elfmachines,verbs=get;list;watch;create;update;patch;delete
Expand Down Expand Up @@ -609,14 +610,14 @@

vmRef := util.GetVMRef(vm)
// If vmRef is in UUID format, it means that the ELF VM created.
if !machineutil.IsUUID(vmRef) {
if !typesutil.IsUUID(vmRef) {
ctx.Logger.Info("The VM is being created", "vmRef", vmRef)

return vm, false, nil
}

// When ELF VM created, set UUID to VMRef
if !machineutil.IsUUID(ctx.ElfMachine.Status.VMRef) {
if !typesutil.IsUUID(ctx.ElfMachine.Status.VMRef) {
ctx.ElfMachine.SetVM(vmRef)
}

Expand Down Expand Up @@ -657,7 +658,7 @@
return nil, err
}

if machineutil.IsUUID(ctx.ElfMachine.Status.VMRef) {
if typesutil.IsUUID(ctx.ElfMachine.Status.VMRef) {

Check warning on line 661 in controllers/elfmachine_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/elfmachine_controller.go#L661

Added line #L661 was not covered by tests
vmDisconnectionTimestamp := ctx.ElfMachine.GetVMDisconnectionTimestamp()
if vmDisconnectionTimestamp == nil {
now := metav1.Now()
Expand Down
5 changes: 3 additions & 2 deletions controllers/elfmachine_controller_placement_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import (
annotationsutil "github.com/smartxworks/cluster-api-provider-elf/pkg/util/annotations"
kcputil "github.com/smartxworks/cluster-api-provider-elf/pkg/util/kcp"
machineutil "github.com/smartxworks/cluster-api-provider-elf/pkg/util/machine"
typesutil "github.com/smartxworks/cluster-api-provider-elf/pkg/util/types"
"github.com/smartxworks/cluster-api-provider-elf/pkg/version"
)

Expand Down Expand Up @@ -258,7 +259,7 @@ func (r *ElfMachineReconciler) getVMHostForRollingUpdate(ctx *context.MachineCon

elfMachineMap := make(map[string]*infrav1.ElfMachine)
for i := 0; i < len(elfMachines); i++ {
if machineutil.IsUUID(elfMachines[i].Status.VMRef) {
if typesutil.IsUUID(elfMachines[i].Status.VMRef) {
elfMachineMap[elfMachines[i].Name] = elfMachines[i]
}
}
Expand Down Expand Up @@ -352,7 +353,7 @@ func (r *ElfMachineReconciler) getPlacementGroup(ctx *context.MachineContext, pl
}

// Placement group is performing an operation
if !machineutil.IsUUID(*placementGroup.LocalID) || placementGroup.EntityAsyncStatus != nil {
if !typesutil.IsUUID(*placementGroup.LocalID) || placementGroup.EntityAsyncStatus != nil {
ctx.Logger.Info("Waiting for placement group task done", "placementGroup", *placementGroup.Name)

return nil, nil
Expand Down
4 changes: 4 additions & 0 deletions pkg/service/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,7 @@ const (
// SKSVMTemplateUIDLabel is the label used to find the virtual machine template.
SKSVMTemplateUIDLabel = "system.cloudtower/sks-template-uid"
)

// VMOwnerSearchForUsername is used to specify the ower source of the virtual machine.
// TODO: Tower SDK will provide the VMOwnerSearchFor enumeration types in the new version.
const VMOwnerSearchForUsername = "username"
25 changes: 25 additions & 0 deletions pkg/service/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (

infrav1 "github.com/smartxworks/cluster-api-provider-elf/api/v1beta1"
"github.com/smartxworks/cluster-api-provider-elf/pkg/config"
typesutil "github.com/smartxworks/cluster-api-provider-elf/pkg/util/types"
)

// GetUpdatedVMRestrictedFields returns the updated restricted fields of the VM compared to ElfMachine.
Expand Down Expand Up @@ -309,3 +310,27 @@ func calGPUAvailableVgpusNum(vgpuInstanceNum, assignedVGPUsNum int32) int32 {

return count
}

// parseOwnerFromCreatedBy parse owner from createdBy annotation.
//
// The owner can be in one of the following two formats:
// 1. ${Tower username}_${Tower auth_config_id}, e.g. caas.smartx_7e98ecbb-779e-43f6-8330-1bc1d29fffc7.
// 2. ${Tower username}, e.g. root. If auth_config_id is not set, it means it is a LOCAL user.
func parseOwnerFromCreatedBy(createdBy string) string {
Copy link
Collaborator

Choose a reason for hiding this comment

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

改个名称:parseOwnerFromCreatedByAnnotation()

lastIndex := strings.LastIndex(createdBy, "@")
if len(createdBy) <= 1 || lastIndex <= 0 || lastIndex == len(createdBy) {
return createdBy
}

username := createdBy[0:lastIndex]
authConfigID := createdBy[lastIndex+1:]

// If authConfigID is not in UUID format, it means username contains the last `@` character,
// return createdBy directly.
if !typesutil.IsUUID(authConfigID) {
return createdBy
}

// last `@` replaced with `_`.
Copy link
Collaborator

Choose a reason for hiding this comment

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

这个替换补充下原因

Copy link
Contributor Author

@haijianyang haijianyang Dec 7, 2023

Choose a reason for hiding this comment

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

func 已经有说明使用 ${Tower username}_${Tower auth_config_id} 这种格式。Annotation 也有说明使用 @ 间隔。

Copy link
Collaborator

Choose a reason for hiding this comment

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

// last @ replaced with _. 只说了 做什么,没说为什么要做

Copy link
Collaborator

Choose a reason for hiding this comment

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

或者你在Func comment中补充下 createdBy string是什么格式

return fmt.Sprintf("%s_%s", username, authConfigID)
}
23 changes: 23 additions & 0 deletions pkg/service/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,3 +261,26 @@ func TestHasGPUsCanNotBeUsedForVM(t *testing.T) {
}), elfMachine)).To(gomega.BeFalse())
})
}

func TestParseOwnerFromCreatedBy(t *testing.T) {
g := gomega.NewGomegaWithT(t)

t.Run("ParseOwnerFromCreatedBy", func(t *testing.T) {
g.Expect(parseOwnerFromCreatedBy("")).To(gomega.Equal(""))
g.Expect(parseOwnerFromCreatedBy("a")).To(gomega.Equal("a"))
g.Expect(parseOwnerFromCreatedBy("@")).To(gomega.Equal("@"))
g.Expect(parseOwnerFromCreatedBy("a@")).To(gomega.Equal("a@"))
g.Expect(parseOwnerFromCreatedBy("@a")).To(gomega.Equal("@a"))
g.Expect(parseOwnerFromCreatedBy("@@")).To(gomega.Equal("@@"))
g.Expect(parseOwnerFromCreatedBy("root")).To(gomega.Equal("root"))
g.Expect(parseOwnerFromCreatedBy("@root")).To(gomega.Equal("@root"))
g.Expect(parseOwnerFromCreatedBy("ro@ot")).To(gomega.Equal("ro@ot"))
g.Expect(parseOwnerFromCreatedBy("root@")).To(gomega.Equal("root@"))
g.Expect(parseOwnerFromCreatedBy("@ro@ot@")).To(gomega.Equal("@ro@ot@"))
g.Expect(parseOwnerFromCreatedBy("root@123456")).To(gomega.Equal("root@123456"))
g.Expect(parseOwnerFromCreatedBy("root@d8dc20fc-e197-41da-83b6-c903c88663fd")).To(gomega.Equal("root_d8dc20fc-e197-41da-83b6-c903c88663fd"))
g.Expect(parseOwnerFromCreatedBy("@root@d8dc20fc-e197-41da-83b6-c903c88663fd")).To(gomega.Equal("@root_d8dc20fc-e197-41da-83b6-c903c88663fd"))
g.Expect(parseOwnerFromCreatedBy("root@@d8dc20fc-e197-41da-83b6-c903c88663fd")).To(gomega.Equal("root@_d8dc20fc-e197-41da-83b6-c903c88663fd"))
g.Expect(parseOwnerFromCreatedBy("root@d8dc20fc-e197-41da-83b6-c903c88663fd@")).To(gomega.Equal("root@d8dc20fc-e197-41da-83b6-c903c88663fd@"))
})
}
11 changes: 11 additions & 0 deletions pkg/service/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
infrav1 "github.com/smartxworks/cluster-api-provider-elf/api/v1beta1"
"github.com/smartxworks/cluster-api-provider-elf/pkg/config"
"github.com/smartxworks/cluster-api-provider-elf/pkg/session"
annotationsutil "github.com/smartxworks/cluster-api-provider-elf/pkg/util/annotations"
)

type VMService interface {
Expand Down Expand Up @@ -241,11 +242,21 @@
hostID = *host.ID
}

var owner *models.VMOwnerParams
if createdBy := annotationsutil.GetCreatedBy(elfCluster); createdBy != "" {
creator := parseOwnerFromCreatedBy(createdBy)
owner = &models.VMOwnerParams{
SearchFor: TowerString(VMOwnerSearchForUsername),
Value: TowerString(creator),
}
}

Check warning on line 252 in pkg/service/vm.go

View check run for this annotation

Codecov / codecov/patch

pkg/service/vm.go#L245-L252

Added lines #L245 - L252 were not covered by tests

vmCreateVMFromTemplateParams := &models.VMCreateVMFromContentLibraryTemplateParams{
ClusterID: cluster.ID,
HostID: TowerString(hostID),
Name: TowerString(elfMachine.Name),
Description: TowerString(fmt.Sprintf(config.VMDescription, elfCluster.Spec.Tower.Server)),
Owner: owner,

Check warning on line 259 in pkg/service/vm.go

View check run for this annotation

Codecov / codecov/patch

pkg/service/vm.go#L259

Added line #L259 was not covered by tests
Vcpu: vCPU,
CPUCores: cpuCores,
CPUSockets: cpuSockets,
Expand Down
9 changes: 9 additions & 0 deletions pkg/util/annotations/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@ func GetPlacementGroupName(o metav1.Object) string {
return annotations[infrav1.PlacementGroupNameAnnotation]
}

func GetCreatedBy(o metav1.Object) string {
annotations := o.GetAnnotations()
if annotations == nil {
return ""
}

return annotations[infrav1.CreatedByAnnotation]
}

// AddAnnotations sets the desired annotations on the object and returns true if the annotations have changed.
func AddAnnotations(o metav1.Object, desired map[string]string) bool {
return annotations.AddAnnotations(o, desired)
Expand Down
17 changes: 2 additions & 15 deletions pkg/util/machine/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (

infrav1 "github.com/smartxworks/cluster-api-provider-elf/api/v1beta1"
labelsutil "github.com/smartxworks/cluster-api-provider-elf/pkg/util/labels"
typesutil "github.com/smartxworks/cluster-api-provider-elf/pkg/util/types"
)

const (
Expand All @@ -39,10 +40,6 @@ const (
// ProviderIDPattern is a regex pattern and is used by ConvertProviderIDToUUID
// to convert a providerID into a UUID string.
ProviderIDPattern = `(?i)^` + ProviderIDPrefix + `([a-f\d]{8}-[a-f\d]{4}-[a-f\d]{4}-[a-f\d]{4}-[a-f\d]{12})$`

// UUIDPattern is a regex pattern and is used by ConvertUUIDToProviderID
// to convert a UUID into a providerID string.
UUIDPattern = `(?i)^[a-f\d]{8}-[a-f\d]{4}-[a-f\d]{4}-[a-f\d]{4}-[a-f\d]{12}$`
)

// ErrNoMachineIPAddr indicates that no valid IP addresses were found in a machine context.
Expand Down Expand Up @@ -132,23 +129,13 @@ func ConvertProviderIDToUUID(providerID *string) string {
}

func ConvertUUIDToProviderID(uuid string) string {
if !IsUUID(uuid) {
if !typesutil.IsUUID(uuid) {
return ""
}

return ProviderIDPrefix + uuid
}

func IsUUID(uuid string) bool {
if uuid == "" {
return false
}

pattern := regexp.MustCompile(UUIDPattern)

return pattern.MatchString(uuid)
}

func GetNetworkStatus(ipsStr string) []infrav1.NetworkStatus {
networks := []infrav1.NetworkStatus{}

Expand Down
43 changes: 0 additions & 43 deletions pkg/util/machine/machine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,49 +183,6 @@ func TestConvertUUIDtoProviderID(t *testing.T) {
}
}

func TestIsUUID(t *testing.T) {
g := gomega.NewGomegaWithT(t)

testCases := []struct {
name string
uuid string
isUUID bool
}{
{
name: "empty uuid",
uuid: "",
isUUID: false,
},
{
name: "invalid uuid",
uuid: "1234",
isUUID: false,
},
{
name: "valid uuid",
uuid: "12345678-1234-1234-1234-123456789abc",
isUUID: true,
},
{
name: "mixed case",
uuid: "12345678-1234-1234-1234-123456789AbC",
isUUID: true,
},
{
name: "invalid hex chars",
uuid: "12345678-1234-1234-1234-123456789abg",
isUUID: false,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
isUUID := IsUUID(tc.uuid)
g.Expect(isUUID).To(gomega.Equal(tc.isUUID))
})
}
}

func TestGetNetworkStatus(t *testing.T) {
g := gomega.NewGomegaWithT(t)

Expand Down
4 changes: 2 additions & 2 deletions pkg/util/tower.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (
"github.com/smartxworks/cloudtower-go-sdk/v2/models"

"github.com/smartxworks/cluster-api-provider-elf/pkg/service"
machineutil "github.com/smartxworks/cluster-api-provider-elf/pkg/util/machine"
typesutil "github.com/smartxworks/cluster-api-provider-elf/pkg/util/types"
)

// GetVMRef returns the ID or localID of the VM.
Expand All @@ -34,7 +34,7 @@ func GetVMRef(vm *models.VM) string {
}

vmLocalID := service.GetTowerString(vm.LocalID)
if machineutil.IsUUID(vmLocalID) {
if typesutil.IsUUID(vmLocalID) {
return vmLocalID
}

Expand Down
35 changes: 35 additions & 0 deletions pkg/util/types/uuid.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
Copyright 2023.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package types

import "regexp"

const (
// UUIDPattern is a regex pattern and is used by ConvertUUIDToProviderID
// to convert a UUID into a providerID string.
UUIDPattern = `(?i)^[a-f\d]{8}-[a-f\d]{4}-[a-f\d]{4}-[a-f\d]{4}-[a-f\d]{12}$`
)

func IsUUID(uuid string) bool {
if uuid == "" {
return false
}

pattern := regexp.MustCompile(UUIDPattern)

return pattern.MatchString(uuid)
}
Loading