Skip to content
This repository has been archived by the owner on Jun 25, 2024. It is now read-only.

Commit

Permalink
Add AnsibleVarsFrom
Browse files Browse the repository at this point in the history
  • Loading branch information
fao89 committed Mar 5, 2024
1 parent 1582c02 commit 6960bad
Show file tree
Hide file tree
Showing 8 changed files with 261 additions and 2 deletions.
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ docs: manifests docs-dependencies crd-to-markdown ## Build docs
$(CRD_MARKDOWN) -f api/v1beta1/common.go -f api/v1beta1/openstackdataplaneservice_types.go -f api/v1beta1/openstackdataplanenodeset_types.go -f api/v1beta1/openstackdataplanedeployment_types.go -n OpenStackDataPlaneService -n OpenStackDataPlaneNodeSet -n OpenStackDataPlaneDeployment > docs/assemblies/custom_resources.md
bundle exec kramdoc --auto-ids docs/assemblies/custom_resources.md && rm docs/assemblies/custom_resources.md
sed -i "s/=== Custom/== Custom/g" docs/assemblies/custom_resources.adoc
cd docs; $(MAKE) html
cd docs; $(MAKE) html BUILD=upstream
cd docs; $(MAKE) html BUILD=downstream

.PHONY: docs-clean
docs-clean:
Expand Down
46 changes: 46 additions & 0 deletions api/bases/dataplane.openstack.org_openstackdataplanenodesets.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,29 @@ spec:
type: string
ansibleVars:
x-kubernetes-preserve-unknown-fields: true
ansibleVarsFrom:
items:
properties:
configMapRef:
properties:
name:
type: string
optional:
type: boolean
type: object
x-kubernetes-map-type: atomic
prefix:
type: string
secretRef:
properties:
name:
type: string
optional:
type: boolean
type: object
x-kubernetes-map-type: atomic
type: object
type: array
type: object
ansibleSSHPrivateKeySecret:
type: string
Expand Down Expand Up @@ -1078,6 +1101,29 @@ spec:
type: string
ansibleVars:
x-kubernetes-preserve-unknown-fields: true
ansibleVarsFrom:
items:
properties:
configMapRef:
properties:
name:
type: string
optional:
type: boolean
type: object
x-kubernetes-map-type: atomic
prefix:
type: string
secretRef:
properties:
name:
type: string
optional:
type: boolean
type: object
x-kubernetes-map-type: atomic
type: object
type: array
type: object
extraMounts:
items:
Expand Down
5 changes: 5 additions & 0 deletions api/v1beta1/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ type AnsibleOpts struct {
// +kubebuilder:pruning:PreserveUnknownFields
// +kubebuilder:validation:Schemaless
AnsibleVars map[string]json.RawMessage `json:"ansibleVars,omitempty"`

// AnsibleVarsFrom is a list of sources to populate ansible variables from.
// Values defined by an AnsibleVars with a duplicate key take precedence.
// +kubebuilder:validation:Optional
AnsibleVarsFrom []corev1.EnvFromSource `json:"ansibleVarsFrom,omitempty"`
}

// NodeSection defines the top level attributes inherited by nodes in the CR.
Expand Down
7 changes: 7 additions & 0 deletions api/v1beta1/zz_generated.deepcopy.go

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

Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,29 @@ spec:
type: string
ansibleVars:
x-kubernetes-preserve-unknown-fields: true
ansibleVarsFrom:
items:
properties:
configMapRef:
properties:
name:
type: string
optional:
type: boolean
type: object
x-kubernetes-map-type: atomic
prefix:
type: string
secretRef:
properties:
name:
type: string
optional:
type: boolean
type: object
x-kubernetes-map-type: atomic
type: object
type: array
type: object
ansibleSSHPrivateKeySecret:
type: string
Expand Down Expand Up @@ -1078,6 +1101,29 @@ spec:
type: string
ansibleVars:
x-kubernetes-preserve-unknown-fields: true
ansibleVarsFrom:
items:
properties:
configMapRef:
properties:
name:
type: string
optional:
type: boolean
type: object
x-kubernetes-map-type: atomic
prefix:
type: string
secretRef:
properties:
name:
type: string
optional:
type: boolean
type: object
x-kubernetes-map-type: atomic
type: object
type: array
type: object
extraMounts:
items:
Expand Down
5 changes: 5 additions & 0 deletions docs/assemblies/custom_resources.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ AnsibleOpts defines a logical grouping of Ansible related configuration options.
| AnsibleVars for configuring ansible
| map[string]json.RawMessage
| false
| ansibleVarsFrom
| AnsibleVarsFrom is a list of sources to populate ansible variables from. Values defined by an AnsibleVars with a duplicate key take precedence.
| []corev1.EnvFromSource
| false
|===
<<custom-resources,Back to Custom Resources>>
Expand Down
60 changes: 60 additions & 0 deletions docs/assemblies/proc_creating-a-set-of-data-plane-nodes.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,67 @@ spec:
----
+
You can copy a sample template from https://github.com/openstack-k8s-operators/dataplane-operator/tree/main/config/samples/nic-config-samples. For information about customizing the template, see link:https://access.redhat.com/documentation/en-us/red_hat_openstack_platform/17.1/html/installing_and_managing_red_hat_openstack_platform_with_director/assembly_configuring-overcloud-networking_installing-director-on-the-undercloud#ref_network-interface-configuration-options_custom-network-interface-templates[Network interface configuration options].
ifeval::["{build}" == "downstream"]
. Register the operating system of the nodes that are not registered to the Red Hat Customer Portal, and enable repositories for your nodes:
+
----
apiVersion: v1
kind: Secret
metadata:
name: subscription-manager
data:
username: <subscription_manager_username>
password: <subscription_manager_password>
----
+
* Replace `<subscription_manager_username>` with the applicable user name.
* Replace `<subscription_manager_password>` with the applicable password.

+
----
apiVersion: v1
kind: Secret
metadata:
name: redhat-registry
data:
username: <registry_username>
password: <registry_password>
----
+
* Replace `<registry_username>` with the applicable user name.
* Replace `<registry_password>` with the applicable password.

+
----
apiVersion: dataplane.openstack.org/v1beta1
kind: OpenStackDataPlaneNodeSet
metadata:
name: openstack-edpm-ipam
spec:
preProvisioned: True
...
nodeTemplate:
ansible:
...
ansibleVars:
edpm_bootstrap_command: |
subscription-manager register --username {{ subscription_manager_username }} --password {{ subscription_manager_password }}
subscription-manager release --set=9.2
subscription-manager repos --disable=*
subscription-manager repos --enable=rhel-9-for-x86_64-baseos-eus-rpms --enable=rhel-9-for-x86_64-appstream-eus-rpms --enable=rhel-9-for-x86_64-highavailability-eus-rpms --enable=openstack-17.1-for-rhel-9-x86_64-rpms --enable=fast-datapath-for-rhel-9-x86_64-rpms --enable=openstack-dev-preview-for-rhel-9-x86_64-rpms
podman login -u {{ registry_username }} -p {{ registry_password }} registry.redhat.io
ansibleVarsFrom:
- prefix: subscription_manager_
secretRef:
name: subscription-manager
- prefix: registry_
secretRef:
name: redhat-registry
----

+
For a complete list of the Red Hat Customer Portal registration commands, see https://access.redhat.com/solutions/253273. For information about how to log into `registry.redhat.io`, see https://access.redhat.com/RegistryAuthentication#creating-registry-service-accounts-6.
endif::[]
. If your nodes are bare metal, you must configure the bare metal template, see xref:con_provisioning-bare-metal-data-plane-nodes_{context}[Provisioning bare metal data plane nodes].

. Optional: The sample `OpenStackDataPlaneNodeSet` CR you copied includes default node configurations under the `nodes` section. You can add additional nodes, and edit the configured values as required. For example, to add node-specific Ansible variables that customize the node, add the following configuration to your `openstack-edpm.yaml` file:
Expand Down
91 changes: 90 additions & 1 deletion pkg/deployment/inventory.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ import (
"strings"

yaml "gopkg.in/yaml.v3"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"

dataplanev1 "github.com/openstack-k8s-operators/dataplane-operator/api/v1beta1"
infranetworkv1 "github.com/openstack-k8s-operators/infra-operator/apis/network/v1beta1"
Expand All @@ -34,13 +37,91 @@ import (
utils "github.com/openstack-k8s-operators/lib-common/modules/common/util"
)

// getAnsibleVarsFrom gets ansible vars from ConfigMap/Secret
func getAnsibleVarsFrom(ctx context.Context, helper *helper.Helper, namespace string, ansible *dataplanev1.AnsibleOpts) (map[string]string, error) {

var (
configMaps = make(map[string]*v1.ConfigMap)
secrets = make(map[string]*v1.Secret)
result = make(map[string]string)
)
client := helper.GetClient()

// AnsibleVars will override AnsibleVarsFrom variables.
// Process AnsibleVarsFrom first then allow AnsibleVars to replace existing values.
for _, varFrom := range ansible.AnsibleVarsFrom {
switch {
case varFrom.ConfigMapRef != nil:
cm := varFrom.ConfigMapRef
name := cm.Name
configMap, ok := configMaps[name]
if !ok {
optional := cm.Optional != nil && *cm.Optional
configMap = &v1.ConfigMap{}
err := client.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, configMap)
if err != nil {
if errors.IsNotFound(err) && optional {
// ignore error when marked optional
continue
}
return result, err
}
configMaps[name] = configMap
}

for k, v := range configMap.Data {
if len(varFrom.Prefix) > 0 {
k = varFrom.Prefix + k
}

result[k] = v
}

case varFrom.SecretRef != nil:
s := varFrom.SecretRef
name := s.Name
secret, ok := secrets[name]
if !ok {
optional := s.Optional != nil && *s.Optional
secret = &v1.Secret{}
err := client.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, secret)
if err != nil {
if errors.IsNotFound(err) && optional {
// ignore error when marked optional
continue
}
return result, err
}
secrets[name] = secret
}

for k, v := range secret.Data {
if len(varFrom.Prefix) > 0 {
k = varFrom.Prefix + k
}
result[k] = string(v)
}
}
}

return result, nil
}

// GenerateNodeSetInventory yields a parsed Inventory for role
func GenerateNodeSetInventory(ctx context.Context, helper *helper.Helper,
instance *dataplanev1.OpenStackDataPlaneNodeSet,
allIPSets map[string]infranetworkv1.IPSet, dnsAddresses []string, defaultImages dataplanev1.DataplaneAnsibleImageDefaults) (string, error) {
inventory := ansible.MakeInventory()
nodeSetGroup := inventory.AddGroup(instance.Name)
err := resolveGroupAnsibleVars(&instance.Spec.NodeTemplate, &nodeSetGroup, defaultImages)
groupVars, err := getAnsibleVarsFrom(ctx, helper, instance.Namespace, &instance.Spec.NodeTemplate.Ansible)
if err != nil {
utils.LogErrorForObject(helper, err, "Could not resolve ansible group vars", instance)
return "", err
}
for k, v := range groupVars {
nodeSetGroup.Vars[k] = v
}
err = resolveGroupAnsibleVars(&instance.Spec.NodeTemplate, &nodeSetGroup, defaultImages)
if err != nil {
utils.LogErrorForObject(helper, err, "Could not resolve ansible group vars", instance)
return "", err
Expand All @@ -58,6 +139,14 @@ func GenerateNodeSetInventory(ctx context.Context, helper *helper.Helper,

for _, node := range instance.Spec.Nodes {
host := nodeSetGroup.AddHost(strings.Split(node.HostName, ".")[0])
hostVars, err := getAnsibleVarsFrom(ctx, helper, instance.Namespace, &node.Ansible)
if err != nil {
utils.LogErrorForObject(helper, err, "Could not resolve ansible host vars", instance)
return "", err
}
for k, v := range hostVars {
host.Vars[k] = v
}
// Use ansible_host if provided else use hostname. Fall back to
// nodeName if all else fails.
if node.Ansible.AnsibleHost != "" {
Expand Down

0 comments on commit 6960bad

Please sign in to comment.