From a7a5f65106654135c0a680e5553ce1e825b94d1b Mon Sep 17 00:00:00 2001 From: Brendan Shephard Date: Tue, 14 Nov 2023 09:42:56 +1000 Subject: [PATCH 1/2] Expose AnsibleMaxFailPercentage and AnyErrorsFatal This change exposes the Ansible configuration options for AnsibleMaxFailPercentage and AnsibleAnyErrorsFatal via the Services CRD. This will be passed to the AnsibleEE pods to tune specific service failure handling. Signed-off-by: Brendan Shephard --- api/v1beta1/openstackdataplaneservice_types.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/api/v1beta1/openstackdataplaneservice_types.go b/api/v1beta1/openstackdataplaneservice_types.go index c8d82238b..868a065d8 100644 --- a/api/v1beta1/openstackdataplaneservice_types.go +++ b/api/v1beta1/openstackdataplaneservice_types.go @@ -76,6 +76,15 @@ type OpenStackDataPlaneServiceSpec struct { // OpenStackAnsibleEERunnerImage image to use as the ansibleEE runner image // +kubebuilder:validation:Optional OpenStackAnsibleEERunnerImage string `json:"openStackAnsibleEERunnerImage,omitempty" yaml:"openStackAnsibleEERunnerImage,omitempty"` + + // AnsibleMaxFailPercentage is used to tune service specific, allowable failure percentages during the Ansible execution + // https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_error_handling.html#setting-a-maximum-failure-percentage + // +kubebuilder:validation:Optional + AnsibleMaxFailPercentage int64 `json:"ansibleMaxFailPercentage,omitempty"` + + // AnyErrorsFatal is used to tune service specific, any_errors_fatal + // +kubebuilder:validation:Optional + AnyErrorsFatal bool `json:"anyErrorsFatal,omitempty"` } // OpenStackDataPlaneServiceStatus defines the observed state of OpenStackDataPlaneService From a400797ed622975dd5ca32b3f8fcf76fe3e5c860 Mon Sep 17 00:00:00 2001 From: Brendan Shephard Date: Tue, 14 Nov 2023 11:42:31 +1000 Subject: [PATCH 2/2] Add handling for max_fail_percentage and any_errors_fatal This change adds handling for the max_fail_percentage and any_errors_fatal arguments. These are provided via --extra-vars as edpm_max_fail_percentage and edpm_any_errors_fatal when used on the OpenStackDataPlaneService CR's. Else, they can be provided globally to all services like any other Ansible variable under ansibleVars on the OpenStackDataPlaneNodeSet. If values are not provided, or values are removed. Then we will default to Ansible default values. Signed-off-by: Brendan Shephard --- ...nstack.org_openstackdataplaneservices.yaml | 10 ++++++ .../openstackdataplaneservice_types.go | 17 +++++++--- api/v1beta1/zz_generated.deepcopy.go | 21 ++++++++++++ ...nstack.org_openstackdataplaneservices.yaml | 10 ++++++ docs/openstack_dataplaneservice.md | 13 ++++++++ pkg/deployment/service.go | 2 +- pkg/util/ansible_execution.go | 32 ++++++++++--------- 7 files changed, 85 insertions(+), 20 deletions(-) diff --git a/api/bases/dataplane.openstack.org_openstackdataplaneservices.yaml b/api/bases/dataplane.openstack.org_openstackdataplaneservices.yaml index 3adcfd10d..88cacad6a 100644 --- a/api/bases/dataplane.openstack.org_openstackdataplaneservices.yaml +++ b/api/bases/dataplane.openstack.org_openstackdataplaneservices.yaml @@ -30,6 +30,16 @@ spec: type: object spec: properties: + ansibleExtraVars: + properties: + ansibleAnyErrorsFatal: + default: false + type: boolean + ansibleMaxFailPercentage: + maximum: 100 + minimum: 1 + type: integer + type: object configMaps: items: type: string diff --git a/api/v1beta1/openstackdataplaneservice_types.go b/api/v1beta1/openstackdataplaneservice_types.go index 868a065d8..5f11de6ef 100644 --- a/api/v1beta1/openstackdataplaneservice_types.go +++ b/api/v1beta1/openstackdataplaneservice_types.go @@ -73,18 +73,27 @@ type OpenStackDataPlaneServiceSpec struct { // +kubebuilder:validation:Optional Secrets []string `json:"secrets,omitempty"` + // AnsibleExtraVars are error handling variables provided via the --extra-vars interface + // +kubebuilder:validation:Optional + AnsibleExtraVars AnsibleExtraVars `json:"ansibleExtraVars,omitempty"` + // OpenStackAnsibleEERunnerImage image to use as the ansibleEE runner image // +kubebuilder:validation:Optional OpenStackAnsibleEERunnerImage string `json:"openStackAnsibleEERunnerImage,omitempty" yaml:"openStackAnsibleEERunnerImage,omitempty"` +} +// AnsibleExtraVars are error handling variables provided via the --extra-vars interface +type AnsibleExtraVars struct { // AnsibleMaxFailPercentage is used to tune service specific, allowable failure percentages during the Ansible execution // https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_error_handling.html#setting-a-maximum-failure-percentage // +kubebuilder:validation:Optional - AnsibleMaxFailPercentage int64 `json:"ansibleMaxFailPercentage,omitempty"` + // +kubebuilder:validation:Minimum=1 + // +kubebuilder:validation:Maximum=100 + AnsibleMaxFailPercentage int `json:"ansibleMaxFailPercentage,omitempty"` - // AnyErrorsFatal is used to tune service specific, any_errors_fatal - // +kubebuilder:validation:Optional - AnyErrorsFatal bool `json:"anyErrorsFatal,omitempty"` + // AnsibleAnyErrorsFatal is used to tune service specific, any_errors_fatal + // +kubebuilder:default=false + AnsibleAnyErrorsFatal *bool `json:"ansibleAnyErrorsFatal,omitempty"` } // OpenStackDataPlaneServiceStatus defines the observed state of OpenStackDataPlaneService diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index a87c87918..9b35394a5 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -69,6 +69,26 @@ func (in *AnsibleEESpec) DeepCopy() *AnsibleEESpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AnsibleExtraVars) DeepCopyInto(out *AnsibleExtraVars) { + *out = *in + if in.AnsibleAnyErrorsFatal != nil { + in, out := &in.AnsibleAnyErrorsFatal, &out.AnsibleAnyErrorsFatal + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AnsibleExtraVars. +func (in *AnsibleExtraVars) DeepCopy() *AnsibleExtraVars { + if in == nil { + return nil + } + out := new(AnsibleExtraVars) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AnsibleOpts) DeepCopyInto(out *AnsibleOpts) { *out = *in @@ -519,6 +539,7 @@ func (in *OpenStackDataPlaneServiceSpec) DeepCopyInto(out *OpenStackDataPlaneSer *out = make([]string, len(*in)) copy(*out, *in) } + in.AnsibleExtraVars.DeepCopyInto(&out.AnsibleExtraVars) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpenStackDataPlaneServiceSpec. diff --git a/config/crd/bases/dataplane.openstack.org_openstackdataplaneservices.yaml b/config/crd/bases/dataplane.openstack.org_openstackdataplaneservices.yaml index 3adcfd10d..88cacad6a 100644 --- a/config/crd/bases/dataplane.openstack.org_openstackdataplaneservices.yaml +++ b/config/crd/bases/dataplane.openstack.org_openstackdataplaneservices.yaml @@ -30,6 +30,16 @@ spec: type: object spec: properties: + ansibleExtraVars: + properties: + ansibleAnyErrorsFatal: + default: false + type: boolean + ansibleMaxFailPercentage: + maximum: 100 + minimum: 1 + type: integer + type: object configMaps: items: type: string diff --git a/docs/openstack_dataplaneservice.md b/docs/openstack_dataplaneservice.md index d59b7fbee..4f9cd0491 100644 --- a/docs/openstack_dataplaneservice.md +++ b/docs/openstack_dataplaneservice.md @@ -9,6 +9,7 @@ * [AnsibleOpts](#ansibleopts) * [NodeSection](#nodesection) * [NodeTemplate](#nodetemplate) +* [AnsibleExtraVars](#ansibleextravars) * [KubeService](#kubeservice) * [OpenStackDataPlaneServiceList](#openstackdataplaneservicelist) * [OpenStackDataPlaneServiceSpec](#openstackdataplaneservicespec) @@ -76,6 +77,17 @@ NodeTemplate is a specification of the node attributes that override top level a [Back to Custom Resources](#custom-resources) +#### AnsibleExtraVars + +AnsibleExtraVars are error handling variables provided via the --extra-vars interface + +| Field | Description | Scheme | Required | +| ----- | ----------- | ------ | -------- | +| ansibleMaxFailPercentage | AnsibleMaxFailPercentage is used to tune service specific, allowable failure percentages during the Ansible execution https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_error_handling.html#setting-a-maximum-failure-percentage | int | false | +| ansibleAnyErrorsFatal | AnsibleAnyErrorsFatal is used to tune service specific, any_errors_fatal | *bool | false | + +[Back to Custom Resources](#custom-resources) + #### KubeService KubeService represents a Kubernetes Service. It is called like this to avoid the extreme overloading of the Service term in this context @@ -124,6 +136,7 @@ OpenStackDataPlaneServiceSpec defines the desired state of OpenStackDataPlaneSer | playbook | Playbook is a path to the playbook that ansible will run on this execution | string | false | | configMaps | ConfigMaps list of ConfigMap names to mount as ExtraMounts for the OpenStackAnsibleEE | []string | false | | secrets | Secrets list of Secret names to mount as ExtraMounts for the OpenStackAnsibleEE | []string | false | +| ansibleExtraVars | AnsibleExtraVars are error handling variables provided via the --extra-vars interface | [AnsibleExtraVars](#ansibleextravars) | false | | openStackAnsibleEERunnerImage | OpenStackAnsibleEERunnerImage image to use as the ansibleEE runner image | string | false | [Back to Custom Resources](#custom-resources) diff --git a/pkg/deployment/service.go b/pkg/deployment/service.go index 2188f6dfd..1eb435a50 100644 --- a/pkg/deployment/service.go +++ b/pkg/deployment/service.go @@ -44,7 +44,7 @@ type ServiceYAML struct { // DeployService service deployment func DeployService(ctx context.Context, helper *helper.Helper, obj client.Object, sshKeySecret string, inventorySecret string, aeeSpec dataplanev1.AnsibleEESpec, foundService dataplanev1.OpenStackDataPlaneService) error { - err := dataplaneutil.AnsibleExecution(ctx, helper, obj, foundService.Spec.Label, sshKeySecret, inventorySecret, foundService.Spec.Play, foundService.Spec.Playbook, aeeSpec) + err := dataplaneutil.AnsibleExecution(ctx, helper, obj, &foundService, sshKeySecret, inventorySecret, aeeSpec) if err != nil { helper.GetLogger().Error(err, fmt.Sprintf("Unable to execute Ansible for %s", foundService.Name)) return err diff --git a/pkg/util/ansible_execution.go b/pkg/util/ansible_execution.go index 5598d57e3..ea999045e 100644 --- a/pkg/util/ansible_execution.go +++ b/pkg/util/ansible_execution.go @@ -40,25 +40,22 @@ func AnsibleExecution( ctx context.Context, helper *helper.Helper, obj client.Object, - label string, + service *dataplanev1.OpenStackDataPlaneService, sshKeySecret string, inventorySecret string, - play string, - playbook string, aeeSpec dataplanev1.AnsibleEESpec, ) error { - var err error var cmdLineArguments strings.Builder - ansibleEE, err := GetAnsibleExecution(ctx, helper, obj, label) + ansibleEE, err := GetAnsibleExecution(ctx, helper, obj, service.Spec.Label) if err != nil && !k8serrors.IsNotFound(err) { return err } if ansibleEE == nil { var executionName string - if len(label) > 0 { - executionName = fmt.Sprintf("%s-%s", label, obj.GetName()) + if len(service.Spec.Label) > 0 { + executionName = fmt.Sprintf("%s-%s", service.Spec.Label, obj.GetName()) } else { executionName = obj.GetName() } @@ -67,7 +64,7 @@ func AnsibleExecution( Name: executionName, Namespace: obj.GetNamespace(), Labels: map[string]string{ - label: string(obj.GetUID()), + service.Spec.Label: string(obj.GetUID()), }, }, } @@ -90,15 +87,23 @@ func AnsibleExecution( if len(aeeSpec.AnsibleSkipTags) > 0 { fmt.Fprintf(&cmdLineArguments, "--skip-tags %s ", aeeSpec.AnsibleSkipTags) } + if service.Spec.AnsibleExtraVars != (dataplanev1.AnsibleExtraVars{}) { + if service.Spec.AnsibleExtraVars.AnsibleMaxFailPercentage != 0 { + fmt.Fprintf(&cmdLineArguments, "--extra-vars edpm_max_fail_percentage=%d ", service.Spec.AnsibleExtraVars.AnsibleMaxFailPercentage) + } + if service.Spec.AnsibleExtraVars.AnsibleAnyErrorsFatal != nil && !*service.Spec.AnsibleExtraVars.AnsibleAnyErrorsFatal { + fmt.Fprintf(&cmdLineArguments, "--extra-vars edpm_any_errors_fatal=%t ", *service.Spec.AnsibleExtraVars.AnsibleAnyErrorsFatal) + } + } if cmdLineArguments.Len() > 0 { ansibleEE.Spec.CmdLine = strings.TrimSpace(cmdLineArguments.String()) } - if len(play) > 0 { - ansibleEE.Spec.Play = play + if len(service.Spec.Play) > 0 { + ansibleEE.Spec.Play = service.Spec.Play } - if len(playbook) > 0 { - ansibleEE.Spec.Playbook = playbook + if len(service.Spec.Playbook) > 0 { + ansibleEE.Spec.Playbook = service.Spec.Playbook } ansibleEEMounts := storage.VolMounts{} @@ -156,7 +161,6 @@ func AnsibleExecution( } return nil - }) if err != nil { @@ -171,7 +175,6 @@ func AnsibleExecution( // label where