Skip to content

Commit

Permalink
Refactor ansible_execution functions
Browse files Browse the repository at this point in the history
This change refactors functions in the ansible_execution.go file to
seperate logic into more intentional functions. This change ensures the
logic within the module is easy to test and follow.

Signed-off-by: Brendan Shephard <[email protected]>
  • Loading branch information
bshephar committed Sep 10, 2024
1 parent 959b902 commit 5b98b3c
Showing 1 changed file with 154 additions and 117 deletions.
271 changes: 154 additions & 117 deletions pkg/dataplane/util/ansible_execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,6 @@ func AnsibleExecution(
nodeSet client.Object,
) error {
var err error
var cmdLineArguments strings.Builder
var inventoryVolume corev1.Volume
var inventoryName string
var inventoryMountPath string
var sshKeyName string
var sshKeyMountPath string
var sshKeyMountSubPath string

const jobRestartPolicy string = "OnFailure"

ansibleEEMounts := storage.VolMounts{}

executionName, labels := GetAnsibleExecutionNameAndLabels(service, deployment.GetName(), nodeSet.GetName())

Expand All @@ -85,16 +74,124 @@ func AnsibleExecution(
return fmt.Errorf("failed to create NetworkAttachment annotation. Error: %w", err)
}

ansibleEE.CheckAeeSpecVars(aeeSpec, deployment, service, nodeSet)

ansibleEEMounts := storage.VolMounts{}
SetAeeSshMounts(nodeSet, service, sshKeySecrets, &ansibleEEMounts)
SetAeeInvMounts(nodeSet, service, inventorySecrets, &ansibleEEMounts)

ansibleEE.ExtraMounts = append(aeeSpec.ExtraMounts, []storage.VolMounts{ansibleEEMounts}...)
ansibleEE.Env = aeeSpec.Env

aeeJob, err := ansibleEE.JobForOpenStackAnsibleEE(helper)
if err != nil {
return err
}

// CreateOrPatch the AnsibleExecution Job
_, err = controllerutil.CreateOrPatch(ctx, helper.GetClient(), aeeJob, func() error {
// Set controller reference on the Job object
err := controllerutil.SetControllerReference(
helper.GetBeforeObject(), aeeJob, helper.GetScheme())
return err
})
if err != nil {
return err
}

return nil
}

// GetAnsibleExecution gets and returns a batchv1 Job with the given
// labels where
// "openstackdataplaneservice": <serviceName>,
// "openstackdataplanedeployment": <deploymentName>,
// "openstackdataplanenodeset": <nodeSetName>,
// If none or more than one is found, return nil and error
func GetAnsibleExecution(ctx context.Context,
helper *helper.Helper, obj client.Object, labelSelector map[string]string,
) (*batchv1.Job, error) {
var err error
ansibleEEs := &batchv1.JobList{}

listOpts := []client.ListOption{
client.InNamespace(obj.GetNamespace()),
}
if len(labelSelector) > 0 {
labels := client.MatchingLabels(labelSelector)
listOpts = append(listOpts, labels)
}
err = helper.GetClient().List(ctx, ansibleEEs, listOpts...)
if err != nil {
return nil, err
}

var ansibleEE *batchv1.Job
if len(ansibleEEs.Items) == 0 {
return nil, k8serrors.NewNotFound(appsv1.Resource("OpenStackAnsibleEE"), fmt.Sprintf("with label %s", labelSelector))
} else if len(ansibleEEs.Items) == 1 {
ansibleEE = &ansibleEEs.Items[0]
} else {
return nil, fmt.Errorf("multiple OpenStackAnsibleEE's found with label %s", labelSelector)
}

return ansibleEE, nil
}

// getAnsibleExecutionNamePrefix compute the name of the AnsibleEE
func getAnsibleExecutionNamePrefix(serviceName string) string {
var executionNamePrefix string
AnsibleExecutionServiceNameLen := apimachineryvalidation.DNS1123LabelMaxLength - 10
if len(serviceName) > AnsibleExecutionServiceNameLen {
executionNamePrefix = serviceName[:AnsibleExecutionServiceNameLen]
} else {
executionNamePrefix = serviceName
}
return executionNamePrefix
}

// GetAnsibleExecutionNameAndLabels Name and Labels of AnsibleEE
func GetAnsibleExecutionNameAndLabels(service *dataplanev1.OpenStackDataPlaneService,
deploymentName string,
nodeSetName string,
) (string, map[string]string) {
executionName := fmt.Sprintf("%s-%s", getAnsibleExecutionNamePrefix(service.Name), deploymentName)
if !service.Spec.DeployOnAllNodeSets {
executionName = fmt.Sprintf("%s-%s", executionName, nodeSetName)
}

if len(executionName) > apimachineryvalidation.DNS1123LabelMaxLength {
executionName = strings.TrimRight(executionName[:apimachineryvalidation.DNS1123LabelMaxLength], "-.")
}

labels := map[string]string{
"openstackdataplaneservice": service.Name,
"openstackdataplanedeployment": deploymentName,
"openstackdataplanenodeset": nodeSetName,
}
return executionName, labels
}

func (a *EEJob) CheckAeeSpecVars(
aeeSpec *dataplanev1.AnsibleEESpec,
deployment *dataplanev1.OpenStackDataPlaneDeployment,
service *dataplanev1.OpenStackDataPlaneService,
nodeSet client.Object,
) {
const jobRestartPolicy string = "OnFailure"

var cmdLineArguments strings.Builder

if aeeSpec.DNSConfig != nil {
ansibleEE.DNSConfig = aeeSpec.DNSConfig
a.DNSConfig = aeeSpec.DNSConfig
}
if len(aeeSpec.OpenStackAnsibleEERunnerImage) > 0 {
ansibleEE.Image = aeeSpec.OpenStackAnsibleEERunnerImage
a.Image = aeeSpec.OpenStackAnsibleEERunnerImage
} else {
ansibleEE.Image = *dataplanev1.ContainerImageDefaults.AnsibleeeImage
a.Image = *dataplanev1.ContainerImageDefaults.AnsibleeeImage
}
if len(aeeSpec.ExtraVars) > 0 {
ansibleEE.ExtraVars = aeeSpec.ExtraVars
a.ExtraVars = aeeSpec.ExtraVars
}
if len(aeeSpec.AnsibleTags) > 0 {
fmt.Fprintf(&cmdLineArguments, "--tags %s ", aeeSpec.AnsibleTags)
Expand All @@ -106,41 +203,57 @@ func AnsibleExecution(
fmt.Fprintf(&cmdLineArguments, "--skip-tags %s ", aeeSpec.AnsibleSkipTags)
}
if len(aeeSpec.ServiceAccountName) > 0 {
ansibleEE.ServiceAccountName = aeeSpec.ServiceAccountName
a.ServiceAccountName = aeeSpec.ServiceAccountName
}
if cmdLineArguments.Len() > 0 {
ansibleEE.CmdLine = strings.TrimSpace(cmdLineArguments.String())
a.CmdLine = strings.TrimSpace(cmdLineArguments.String())
}

if len(service.Spec.PlaybookContents) > 0 {
ansibleEE.PlaybookContents = service.Spec.PlaybookContents
a.PlaybookContents = service.Spec.PlaybookContents
}
if len(service.Spec.Playbook) > 0 {
ansibleEE.Playbook = service.Spec.Playbook
a.Playbook = service.Spec.Playbook
}
ansibleEE.BackoffLimit = deployment.Spec.BackoffLimit
ansibleEE.RestartPolicy = jobRestartPolicy
a.BackoffLimit = deployment.Spec.BackoffLimit
a.RestartPolicy = jobRestartPolicy

// If we have a service that ought to be deployed everywhere
// substitute the existing play target with 'all'
// Check if we have ExtraVars before accessing it
if ansibleEE.ExtraVars == nil {
ansibleEE.ExtraVars = make(map[string]json.RawMessage)
if a.ExtraVars == nil {
a.ExtraVars = make(map[string]json.RawMessage)
}
if service.Spec.DeployOnAllNodeSets {
ansibleEE.ExtraVars["edpm_override_hosts"] = json.RawMessage([]byte("\"all\""))
a.ExtraVars["edpm_override_hosts"] = json.RawMessage([]byte("\"all\""))
} else {
ansibleEE.ExtraVars["edpm_override_hosts"] = json.RawMessage([]byte(fmt.Sprintf("\"%s\"", nodeSet.GetName())))
a.ExtraVars["edpm_override_hosts"] = json.RawMessage([]byte(fmt.Sprintf("\"%s\"", nodeSet.GetName())))
}
if service.Spec.EDPMServiceType != "" {
ansibleEE.ExtraVars["edpm_service_type"] = json.RawMessage([]byte(fmt.Sprintf("\"%s\"", service.Spec.EDPMServiceType)))
a.ExtraVars["edpm_service_type"] = json.RawMessage([]byte(fmt.Sprintf("\"%s\"", service.Spec.EDPMServiceType)))
} else {
ansibleEE.ExtraVars["edpm_service_type"] = json.RawMessage([]byte(fmt.Sprintf("\"%s\"", service.Name)))
a.ExtraVars["edpm_service_type"] = json.RawMessage([]byte(fmt.Sprintf("\"%s\"", service.Name)))
}

if len(deployment.Spec.ServicesOverride) > 0 {
ansibleEE.ExtraVars["edpm_services_override"] = json.RawMessage([]byte(fmt.Sprintf("\"%s\"", deployment.Spec.ServicesOverride)))
a.ExtraVars["edpm_services_override"] = json.RawMessage([]byte(fmt.Sprintf("\"%s\"", deployment.Spec.ServicesOverride)))
}
}

// SetAeeSshMounts - Using the information provided from the NodeSet, Service and AnsibleEE Spec. We determine the required
// ssh key mounts that are required for the Ansible Execution Job. This function takes a pointer to the storage.VolMounts
// struct and updates them as per the required ssh key related mounts.
func SetAeeSshMounts(
nodeSet client.Object,
service *dataplanev1.OpenStackDataPlaneService,
sshKeySecrets map[string]string,
ansibleEEMounts *storage.VolMounts,
) {
var (
sshKeyName string
sshKeyMountPath string
sshKeyMountSubPath string
)

// Sort keys of the ssh secret map
sshKeys := make([]string, 0)
Expand Down Expand Up @@ -186,6 +299,19 @@ func AnsibleExecution(
ansibleEEMounts.Mounts = append(ansibleEEMounts.Mounts, sshKeyMount)
ansibleEEMounts.Volumes = append(ansibleEEMounts.Volumes, sshKeyVolume)
}
}

func SetAeeInvMounts(
nodeSet client.Object,
service *dataplanev1.OpenStackDataPlaneService,
inventorySecrets map[string]string,
ansibleEEMounts *storage.VolMounts,
) {
var (
inventoryVolume corev1.Volume
inventoryName string
inventoryMountPath string
)

// order the inventory keys otherwise it could lead to changing order and mount order changing
invKeys := make([]string, 0)
Expand Down Expand Up @@ -230,93 +356,4 @@ func AnsibleExecution(
ansibleEEMounts.Mounts = append(ansibleEEMounts.Mounts, inventoryMount)
ansibleEEMounts.Volumes = append(ansibleEEMounts.Volumes, inventoryVolume)
}

ansibleEE.ExtraMounts = append(aeeSpec.ExtraMounts, []storage.VolMounts{ansibleEEMounts}...)
ansibleEE.Env = aeeSpec.Env

aeeJob, err := ansibleEE.JobForOpenStackAnsibleEE(helper)
if err != nil {
return err
}

// CreateOrPatch the AnsibleExecution Job
_, err = controllerutil.CreateOrPatch(ctx, helper.GetClient(), aeeJob, func() error {
// Set controller reference on the Job object
err := controllerutil.SetControllerReference(
helper.GetBeforeObject(), aeeJob, helper.GetScheme())
return err
})
if err != nil {
return err
}

return nil
}

// GetAnsibleExecution gets and returns a batchv1 Job with the given
// labels where
// "openstackdataplaneservice": <serviceName>,
// "openstackdataplanedeployment": <deploymentName>,
// "openstackdataplanenodeset": <nodeSetName>,
// If none or more than one is found, return nil and error
func GetAnsibleExecution(ctx context.Context,
helper *helper.Helper, obj client.Object, labelSelector map[string]string) (*batchv1.Job, error) {
var err error
ansibleEEs := &batchv1.JobList{}

listOpts := []client.ListOption{
client.InNamespace(obj.GetNamespace()),
}
if len(labelSelector) > 0 {
labels := client.MatchingLabels(labelSelector)
listOpts = append(listOpts, labels)
}
err = helper.GetClient().List(ctx, ansibleEEs, listOpts...)
if err != nil {
return nil, err
}

var ansibleEE *batchv1.Job
if len(ansibleEEs.Items) == 0 {
return nil, k8serrors.NewNotFound(appsv1.Resource("OpenStackAnsibleEE"), fmt.Sprintf("with label %s", labelSelector))
} else if len(ansibleEEs.Items) == 1 {
ansibleEE = &ansibleEEs.Items[0]
} else {
return nil, fmt.Errorf("multiple OpenStackAnsibleEE's found with label %s", labelSelector)
}

return ansibleEE, nil
}

// getAnsibleExecutionNamePrefix compute the name of the AnsibleEE
func getAnsibleExecutionNamePrefix(serviceName string) string {
var executionNamePrefix string
AnsibleExecutionServiceNameLen := apimachineryvalidation.DNS1123LabelMaxLength - 10
if len(serviceName) > AnsibleExecutionServiceNameLen {
executionNamePrefix = serviceName[:AnsibleExecutionServiceNameLen]
} else {
executionNamePrefix = serviceName
}
return executionNamePrefix
}

// GetAnsibleExecutionNameAndLabels Name and Labels of AnsibleEE
func GetAnsibleExecutionNameAndLabels(service *dataplanev1.OpenStackDataPlaneService,
deploymentName string,
nodeSetName string) (string, map[string]string) {
executionName := fmt.Sprintf("%s-%s", getAnsibleExecutionNamePrefix(service.Name), deploymentName)
if !service.Spec.DeployOnAllNodeSets {
executionName = fmt.Sprintf("%s-%s", executionName, nodeSetName)
}

if len(executionName) > apimachineryvalidation.DNS1123LabelMaxLength {
executionName = strings.TrimRight(executionName[:apimachineryvalidation.DNS1123LabelMaxLength], "-.")
}

labels := map[string]string{
"openstackdataplaneservice": service.Name,
"openstackdataplanedeployment": deploymentName,
"openstackdataplanenodeset": nodeSetName,
}
return executionName, labels
}

0 comments on commit 5b98b3c

Please sign in to comment.