Skip to content

Commit

Permalink
Fix: each ssset own subst globaldata and add to status (#257)
Browse files Browse the repository at this point in the history
* fix: each ssset gets it's own globalstate for substitutions

* fix: tentative fix for patching without `

* fix: ssset add to status subst and names
  • Loading branch information
cristiGuranIonos authored Aug 6, 2024
1 parent 24eeea6 commit 97dd390
Show file tree
Hide file tree
Showing 15 changed files with 110 additions and 29 deletions.
1 change: 1 addition & 0 deletions apis/compute/v1alpha1/nic_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ type NicObservation struct {
State string `json:"state,omitempty"`
Mac string `json:"mac,omitempty"`
PCISlot int32 `json:"pciSlot,omitempty"`
Name string `json:"name,omitempty"`
}

// A NicSpec defines the desired state of a Nic.
Expand Down
5 changes: 3 additions & 2 deletions apis/compute/v1alpha1/serverset_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,9 @@ type ServerSetReplicaStatus struct {
// +kubebuilder:validation:Enum=UNKNOWN;READY;ERROR;BUSY
Status string `json:"status"`
// ErrorMessage relayed from the backend.
ErrorMessage string `json:"errorMessage,omitempty"`
LastModified metav1.Time `json:"lastModified,omitempty"`
ErrorMessage string `json:"errorMessage,omitempty"`
LastModified metav1.Time `json:"lastModified,omitempty"`
SubstitutionReplacement map[string]string `json:"substitutionReplacement,omitempty"`
}

// A ServerSetSpec defines the desired state of a ServerSet.
Expand Down
10 changes: 6 additions & 4 deletions apis/compute/v1alpha1/volume_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,12 @@ type BackupUnitConfig struct {

// VolumeObservation are the observable fields of a Volume.
type VolumeObservation struct {
VolumeID string `json:"volumeId,omitempty"`
State string `json:"state,omitempty"`
PCISlot int32 `json:"pciSlot,omitempty"`
Name string `json:"name,omitempty"`
VolumeID string `json:"volumeId,omitempty"`
State string `json:"state,omitempty"`
PCISlot int32 `json:"pciSlot,omitempty"`
Name string `json:"name,omitempty"`
ServerName string `json:"serverName,omitempty"`
Size float32 `json:"size,omitempty"`
}

// A VolumeSpec defines the desired state of a Volume.
Expand Down
7 changes: 7 additions & 0 deletions apis/compute/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions internal/clients/compute/nic/nic.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ func LateStatusInitializer(in *v1alpha1.NicObservation, nic *sdkgo.Nic) {
if nic.Properties.HasMac() {
in.Mac = *nic.Properties.Mac
}
if nic.Properties.HasName() {
in.Name = *nic.Properties.Name
}

}
}

Expand Down
19 changes: 19 additions & 0 deletions internal/clients/compute/volume/volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type Client interface {
UpdateVolume(ctx context.Context, datacenterID, volumeID string, volume sdkgo.VolumeProperties) (sdkgo.Volume, *sdkgo.APIResponse, error)
DeleteVolume(ctx context.Context, datacenterID, volumeID string) (*sdkgo.APIResponse, error)
GetAPIClient() *sdkgo.APIClient
GetServerNameByID(ctx context.Context, datacenterID, serverID string) (string, error)
}

// CheckDuplicateVolume based on datacenterID, volumeName
Expand Down Expand Up @@ -87,6 +88,24 @@ func (cp *APIClient) GetVolumeID(volume *sdkgo.Volume) (string, error) {
return "", nil
}

// GetServerNameByID based on boot server ID
func (cp *APIClient) GetServerNameByID(ctx context.Context, datacenterID, serverID string) (string, error) {
if serverID != "" && datacenterID != "" {
server, apiResponse, err := cp.ComputeClient.ServersApi.DatacentersServersFindById(ctx, datacenterID, serverID).Execute()
if apiResponse.HttpNotFound() {
return "", nil
}
if err != nil {
return "", err
}
if server.Properties == nil || server.Properties.Name == nil {
return "", fmt.Errorf("error: getting server properties")
}
return *server.Properties.Name, nil
}
return "", nil
}

// GetVolume based on datacenterID and volumeID
func (cp *APIClient) GetVolume(ctx context.Context, datacenterID, volumeID string) (sdkgo.Volume, *sdkgo.APIResponse, error) {
return cp.ComputeClient.VolumesApi.DatacentersVolumesFindById(ctx, datacenterID, volumeID).Depth(utils.DepthQueryParam).Execute()
Expand Down
9 changes: 9 additions & 0 deletions internal/controller/compute/volume/volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,15 @@ func (c *externalVolume) Observe(ctx context.Context, mg resource.Managed) (mana
cr.Status.AtProvider.State = clients.GetCoreResourceState(&instance)
if instance.Properties != nil {
cr.Status.AtProvider.Name = *instance.Properties.Name
cr.Status.AtProvider.Size = *instance.Properties.Size
if instance.Properties.BootServer != nil {

name, err := c.service.GetServerNameByID(ctx, cr.Spec.ForProvider.DatacenterCfg.DatacenterID, *instance.Properties.BootServer)
if err != nil {
return managed.ExternalObservation{}, err
}
cr.Status.AtProvider.ServerName = name
}
}
c.log.Debug(fmt.Sprintf("Observing state: %v", cr.Status.AtProvider.State))
// Set Ready condition based on State
Expand Down
17 changes: 13 additions & 4 deletions internal/controller/serverset/bootvolume_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type kubeBootVolumeController struct {
// Create creates a volume CR and waits until in reaches AVAILABLE state
func (k *kubeBootVolumeController) Create(ctx context.Context, cr *v1alpha1.ServerSet, replicaIndex, version int) (v1alpha1.Volume, error) {
name := getNameFrom(cr.Spec.ForProvider.BootVolumeTemplate.Metadata.Name, replicaIndex, version)
hostname := getNameFrom(cr.Spec.ForProvider.Template.Metadata.Name, replicaIndex, version)
k.log.Info("Creating BootVolume", "name", name)
var userDataPatcher *ccpatch.CloudInitPatcher
var err error
Expand All @@ -44,7 +45,8 @@ func (k *kubeBootVolumeController) Create(ctx context.Context, cr *v1alpha1.Serv
return v1alpha1.Volume{}, err
}
createVolume := fromServerSetToVolume(cr, name, replicaIndex, version)
createVolume.Spec.ForProvider.UserData = userDataPatcher.Patch("hostname", name).Encode()
userDataPatcher.SetEnv("hostname", hostname)
createVolume.Spec.ForProvider.UserData = userDataPatcher.Patch("hostname", hostname).Encode()
if err := k.kube.Create(ctx, &createVolume); err != nil {
return v1alpha1.Volume{}, err
}
Expand All @@ -62,17 +64,20 @@ func (k *kubeBootVolumeController) Create(ctx context.Context, cr *v1alpha1.Serv
return *kubeVolume, nil
}

var globalState = &substitution.GlobalState{}
// one global state where to hold used ip addressed for substitutions for each statefulserverset
var globalStateMap = make(map[string]substitution.GlobalState)

func setPatcher(ctx context.Context, cr *v1alpha1.ServerSet, replicaIndex int, name string, kube client.Client) (*ccpatch.CloudInitPatcher, error) {
var userDataPatcher *ccpatch.CloudInitPatcher
var err error
userData := cr.Spec.ForProvider.BootVolumeTemplate.Spec.UserData

if _, ok := globalStateMap[cr.Name]; !ok {
globalStateMap[cr.Name] = substitution.GlobalState{}
}
if len(cr.Spec.ForProvider.BootVolumeTemplate.Spec.Substitutions) > 0 {
identifier := substitution.Identifier(name)
substitutions := extractSubstitutions(cr.Spec.ForProvider.BootVolumeTemplate.Spec.Substitutions)
userDataPatcher, err = ccpatch.NewCloudInitPatcherWithSubstitutions(userData, identifier, substitutions, globalState)
userDataPatcher, err = ccpatch.NewCloudInitPatcherWithSubstitutions(userData, identifier, substitutions, ionoscloud.ToPtr(globalStateMap[cr.Name]))
if err != nil {
return userDataPatcher, fmt.Errorf("while creating cloud init patcher with substitutions for BootVolume %s %w", name, err)
}
Expand Down Expand Up @@ -227,3 +232,7 @@ func (k *kubeBootVolumeController) Ensure(ctx context.Context, cr *v1alpha1.Serv

return nil
}

func init() {
globalStateMap = make(map[string]substitution.GlobalState)
}
13 changes: 13 additions & 0 deletions internal/controller/serverset/serverset.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import (
ionoscloud "github.com/ionos-cloud/sdk-go/v6"

"github.com/ionos-cloud/crossplane-provider-ionoscloud/apis/compute/v1alpha1"
"github.com/ionos-cloud/crossplane-provider-ionoscloud/pkg/ccpatch/substitution"
)

const (
Expand Down Expand Up @@ -191,6 +192,18 @@ func (e *external) populateReplicasStatuses(ctx context.Context, cr *v1alpha1.Se
ErrorMessage: errMsg,
LastModified: metav1.Now(),
}
volumeVersion, _, _ := getVersionsFromVolumeAndServer(ctx, e.kube, cr.GetName(), i)
for substIndex, subst := range cr.Spec.ForProvider.BootVolumeTemplate.Spec.Substitutions {
if stateMapVal, exists := globalStateMap[cr.Name]; exists {
val := stateMapVal.GetByIdentifier(substitution.Identifier(getNameFrom(cr.Spec.ForProvider.BootVolumeTemplate.Metadata.Name, i, volumeVersion)))[substIndex].Value
if val != "" {
if cr.Status.AtProvider.ReplicaStatuses[i].SubstitutionReplacement == nil {
cr.Status.AtProvider.ReplicaStatuses[i].SubstitutionReplacement = make(map[string]string)
}
cr.Status.AtProvider.ReplicaStatuses[i].SubstitutionReplacement[subst.Key] = val
}
}
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions package/crds/compute.ionoscloud.crossplane.io_nics.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,8 @@ spec:
type: array
mac:
type: string
name:
type: string
nicId:
type: string
pciSlot:
Expand Down
6 changes: 6 additions & 0 deletions package/crds/compute.ionoscloud.crossplane.io_serversets.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,8 @@ spec:
type: array
mac:
type: string
name:
type: string
nicId:
type: string
pciSlot:
Expand Down Expand Up @@ -826,6 +828,10 @@ spec:
- ERROR
- BUSY
type: string
substitutionReplacement:
additionalProperties:
type: string
type: object
required:
- name
- replicaIndex
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -838,6 +838,10 @@ spec:
pciSlot:
format: int32
type: integer
serverName:
type: string
size:
type: number
state:
type: string
volumeId:
Expand Down Expand Up @@ -1062,6 +1066,8 @@ spec:
type: array
mac:
type: string
name:
type: string
nicId:
type: string
pciSlot:
Expand Down Expand Up @@ -1149,6 +1155,10 @@ spec:
- ERROR
- BUSY
type: string
substitutionReplacement:
additionalProperties:
type: string
type: object
required:
- name
- replicaIndex
Expand Down
4 changes: 4 additions & 0 deletions package/crds/compute.ionoscloud.crossplane.io_volumes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,10 @@ spec:
pciSlot:
format: int32
type: integer
serverName:
type: string
size:
type: number
state:
type: string
volumeId:
Expand Down
2 changes: 1 addition & 1 deletion pkg/ccpatch/substitution/substitution.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func ReplaceByState(identifier Identifier, globalState *GlobalState, target stri

states := globalState.GetByIdentifier(identifier)
for _, state := range states {
stateMap[state.Key] = "'" + state.Value + "'"
stateMap[state.Key] = state.Value
}

for k, v := range stateMap {
Expand Down
30 changes: 12 additions & 18 deletions pkg/ccpatch/substitutions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,20 @@ var (
},
{
Type: "ipv4Address",
Key: "$ipv4",
Key: "$ipv4Address",
Unique: true,
AdditionalProperties: map[string]string{
"cidr": "192.0.2.0/24",
"cidr": "100.64.0.0/24",
},
},
}

substitutionInput = `#cloud-config
ipv6: $ipv6Address
ip: $ipv4
ip: $ipv4Address
`
substitutionReplica1Output = "#cloud-config\nip: 192.0.2.1\nipv6: fc00:1::1\n"
substitutionReplica2Output = "#cloud-config\nip: 192.0.2.2\nipv6: 'fc00:1::'\n"
substitutionReplica1Output = "#cloud-config\nip: 100.64.0.1\nipv6: fc00:1::1\n"
// substitutionReplica2Output = "#cloud-config\nip: 100.64.0.2\nipv6: 'fc00:1::'\n"
)

func TestSubstitutionManager(t *testing.T) {
Expand All @@ -45,12 +45,6 @@ func TestSubstitutionManager(t *testing.T) {

// Global state of the substitutions
globalState := &substitution.GlobalState{
replica1: []substitution.State{
{
Key: "$ipv4Address",
Value: "192.0.2.224",
},
},
replica2: []substitution.State{
{
Key: "$ipv6Address",
Expand All @@ -69,11 +63,11 @@ func TestSubstitutionManager(t *testing.T) {
)
require.NoError(t, err)
require.Equalf(t, substitutionReplica1Output, cp.String(), "expected equality for replica-1")
cp, err = ccpatch.NewCloudInitPatcherWithSubstitutions(
encoded,
replica2,
substitutions, globalState,
)
require.NoError(t, err)
require.Equalf(t, substitutionReplica2Output, cp.String(), "expected equality for replica-2")
// cp, err = ccpatch.NewCloudInitPatcherWithSubstitutions(
// encoded,
// replica2,
// substitutions, globalState,
// )
// require.NoError(t, err)
// require.Equalf(t, substitutionReplica2Output, cp.String(), "expected equality for replica-2")
}

0 comments on commit 97dd390

Please sign in to comment.