diff --git a/config/crd/bases/airship.airshipit.org_vinoes.yaml b/config/crd/bases/airship.airshipit.org_vinoes.yaml index 5fa782b..86fffb7 100644 --- a/config/crd/bases/airship.airshipit.org_vinoes.yaml +++ b/config/crd/bases/airship.airshipit.org_vinoes.yaml @@ -149,6 +149,15 @@ spec: name: description: Parameter for Node master or worker-standard type: string + networkDataTemplate: + description: NetworkDataTemplate reference a Secret containing + a template key + properties: + name: + type: string + namespace: + type: string + type: object networkInterfaces: description: NetworkInterface define interface on the VM properties: diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 85aefb1..1d3e7b7 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -21,6 +21,17 @@ rules: verbs: - list - watch +- apiGroups: + - "" + resources: + - secrets + verbs: + - create + - get + - list + - patch + - update + - watch - apiGroups: - airship.airshipit.org resources: diff --git a/config/samples/network-template-secret.yaml b/config/samples/network-template-secret.yaml new file mode 100644 index 0000000..417573c --- /dev/null +++ b/config/samples/network-template-secret.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: Secret +metadata: + name: test-template + namespace: default +type: Opaque +stringData: + template: REPLACEME \ No newline at end of file diff --git a/config/samples/vino_cr.yaml b/config/samples/vino_cr.yaml index a138d35..ff61b1d 100644 --- a/config/samples/vino_cr.yaml +++ b/config/samples/vino_cr.yaml @@ -31,3 +31,7 @@ spec: nodes: - name: "worker" count: 3 + networkDataTemplate: + name: "test-template" + namespace: "default" + diff --git a/docs/api/vino.md b/docs/api/vino.md index 9670c78..5a057cb 100644 --- a/docs/api/vino.md +++ b/docs/api/vino.md @@ -688,6 +688,19 @@ DiskDrivesTemplate + + +networkDataTemplate
+ + +NamespacedName + + + + +

NetworkDataTemplate reference a Secret containing a template key

+ + diff --git a/pkg/api/v1/vino_types.go b/pkg/api/v1/vino_types.go index e5d4654..d8906d5 100644 --- a/pkg/api/v1/vino_types.go +++ b/pkg/api/v1/vino_types.go @@ -83,12 +83,14 @@ type VMRoutes struct { //NodeSet node definitions type NodeSet struct { //Parameter for Node master or worker-standard - Name string `json:"name,omitempty"` - Count int `json:"count,omitempty"` - NodeLabel *VMNodeFlavor `json:"labels,omitempty"` - LibvirtTemplateDefinition NamespacedName `json:"libvirtTemplate,omitempty"` - NetworkInterface *NetworkInterface `json:"networkInterfaces,omitempty"` - DiskDrives *DiskDrivesTemplate `json:"diskDrives,omitempty"` + Name string `json:"name,omitempty"` + Count int `json:"count,omitempty"` + NodeLabel *VMNodeFlavor `json:"labels,omitempty"` + LibvirtTemplate NamespacedName `json:"libvirtTemplate,omitempty"` + NetworkInterface *NetworkInterface `json:"networkInterfaces,omitempty"` + DiskDrives *DiskDrivesTemplate `json:"diskDrives,omitempty"` + // NetworkDataTemplate reference a Secret containing a template key + NetworkDataTemplate NamespacedName `json:"networkDataTemplate,omitempty"` } // VMNodeFlavor labels for node to be annotated diff --git a/pkg/api/v1/zz_generated.deepcopy.go b/pkg/api/v1/zz_generated.deepcopy.go index 011f9f1..cd6f743 100644 --- a/pkg/api/v1/zz_generated.deepcopy.go +++ b/pkg/api/v1/zz_generated.deepcopy.go @@ -282,7 +282,7 @@ func (in *NodeSet) DeepCopyInto(out *NodeSet) { *out = new(VMNodeFlavor) (*in).DeepCopyInto(*out) } - out.LibvirtTemplateDefinition = in.LibvirtTemplateDefinition + out.LibvirtTemplate = in.LibvirtTemplate if in.NetworkInterface != nil { in, out := &in.NetworkInterface, &out.NetworkInterface *out = new(NetworkInterface) @@ -293,6 +293,7 @@ func (in *NodeSet) DeepCopyInto(out *NodeSet) { *out = new(DiskDrivesTemplate) (*in).DeepCopyInto(*out) } + out.NetworkDataTemplate = in.NetworkDataTemplate } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeSet. diff --git a/pkg/controllers/bmh.go b/pkg/controllers/bmh.go index 716904c..087e84c 100644 --- a/pkg/controllers/bmh.go +++ b/pkg/controllers/bmh.go @@ -1,8 +1,26 @@ +/* + + +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 controllers import ( + "bytes" "context" "fmt" + "text/template" "github.com/go-logr/logr" metal3 "github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1" @@ -97,7 +115,7 @@ func (r *VinoReconciler) createBMHperPod(ctx context.Context, vino *vinov1.Vino, return err } - netData, netDataNs, err := r.reconcileBMHNetworkData(ctx, vino) + netData, netDataNs, err := r.reconcileBMHNetworkData(ctx, node, vino, nil) if err != nil { return err } @@ -168,8 +186,62 @@ func (r *VinoReconciler) reconcileBMHCredentials(ctx context.Context, vino *vino return "credentials", nil } -//nolint:unparam -func (r *VinoReconciler) reconcileBMHNetworkData(_ context.Context, vino *vinov1.Vino) (string, string, error) { - // TODO implement this - return "network-data", getRuntimeNamespace(), nil +func (r *VinoReconciler) reconcileBMHNetworkData( + ctx context.Context, + node vinov1.NodeSet, + vino *vinov1.Vino, + values interface{}) (string, string, error) { + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: node.NetworkDataTemplate.Name, + Namespace: node.NetworkDataTemplate.Namespace, + }, + } + + logger := logr.FromContext(ctx).WithValues("vino node", node.Name, "vino", client.ObjectKeyFromObject(vino)) + + objKey := client.ObjectKeyFromObject(secret) + logger.Info("Looking for secret with network template for vino node", "secret", objKey) + if err := r.Get(ctx, objKey, secret); err != nil { + return "", "", err + } + + rawTmpl, ok := secret.Data[TemplateDefaultKey] + if !ok { + return "", "", fmt.Errorf("network template secret %v has no key '%s'", objKey, TemplateDefaultKey) + } + + tpl, err := template.New("net-template").Parse(string(rawTmpl)) + if err != nil { + return "", "", err + } + + buf := bytes.NewBuffer([]byte{}) + err = tpl.Execute(buf, values) + if err != nil { + return "", "", err + } + + name := fmt.Sprintf("%s-%s-%s", vino.Namespace, vino.Name, node.Name) + + ns := getRuntimeNamespace() + netSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + }, + StringData: map[string]string{ + "networkData": buf.String(), + }, + Type: corev1.SecretTypeOpaque, + } + + objKey = client.ObjectKeyFromObject(netSecret) + + logger.Info("Creating network secret for vino node", "secret", objKey) + + if err := applyRuntimeObject(ctx, objKey, netSecret, r.Client); err != nil { + return "", "", err + } + return name, ns, nil } diff --git a/pkg/controllers/bmh_test.go b/pkg/controllers/bmh_test.go index e5c3177..f62339c 100644 --- a/pkg/controllers/bmh_test.go +++ b/pkg/controllers/bmh_test.go @@ -1,3 +1,19 @@ +/* + + +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 controllers import ( @@ -28,6 +44,10 @@ var _ = Describe("Test BMH reconciliation", func() { { Name: "worker", Count: 3, + NetworkDataTemplate: vinov1.NamespacedName{ + Name: "default-template", + Namespace: "default", + }, }, } @@ -63,6 +83,17 @@ var _ = Describe("Test BMH reconciliation", func() { }, } + networkTmplSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default-template", + Namespace: "default", + }, + Type: corev1.SecretTypeOpaque, + Data: map[string][]byte{ + TemplateDefaultKey: []byte("REPLACEME"), + }, + } + node1 := &corev1.Node{ ObjectMeta: metav1.ObjectMeta{ Name: "node01", @@ -92,7 +123,7 @@ var _ = Describe("Test BMH reconciliation", func() { fake.NewClientBuilder() reconciler := &VinoReconciler{ - Client: fake.NewFakeClient(podList, node1, node2, vino), + Client: fake.NewFakeClient(podList, node1, node2, vino, networkTmplSecret), } l := zap.New(zap.UseDevMode(true)) @@ -107,8 +138,18 @@ var _ = Describe("Test BMH reconciliation", func() { Namespace: "vino-system", }, } + + networkSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default-vino-worker", + Namespace: "vino-system", + }, + } + Expect(reconciler.Get(ctx, client.ObjectKeyFromObject(bmh), bmh)).Should(Succeed()) Expect(bmh.Spec.BMC.Address).To(Equal("redfish+http://10.0.0.2:8000/redfish/v1/Systems/worker-1")) + Expect(reconciler.Get(ctx, client.ObjectKeyFromObject(networkSecret), networkSecret)).Should(Succeed()) + Expect(networkSecret.StringData["networkData"]).To(Equal("REPLACEME")) }) }) }) diff --git a/pkg/controllers/vino_controller.go b/pkg/controllers/vino_controller.go index e8dab35..4442b0e 100644 --- a/pkg/controllers/vino_controller.go +++ b/pkg/controllers/vino_controller.go @@ -42,164 +42,11 @@ import ( ) const ( - DaemonSetTemplateDefaultDataKey = "template" - DaemonSetTemplateDefaultName = "vino-daemonset-template" + TemplateDefaultKey = "template" + DaemonSetTemplateDefaultName = "vino-daemonset-template" ContainerNameLibvirt = "libvirt" ConfigMapKeyVinoSpec = "vino-spec" - - // TODO (alexanderhughes) Enable this section of code when ready to integrate the BMH creation -// sushyDataContent = ` -//{ -// "username": "foo", -// "password": "bar", -//}` -// -// networkDataContent = ` -//{ -// "links": [ -// { -// "id": "eno4", -// "name": "eno4", -// "type": "phy", -// "mtu": 1500 -// }, -// { -// "id": "enp59s0f1", -// "name": "enp59s0f1", -// "type": "phy", -// "mtu": 9100 -// }, -// { -// "id": "enp216s0f0", -// "name": "enp216s0f0", -// "type": "phy", -// "mtu": 9100 -// }, -// { -// "id": "bond0", -// "name": "bond0", -// "type": "bond", -// "bond_links": [ -// "enp59s0f1", -// "enp216s0f0" -// ], -// "bond_mode": "802.3ad", -// "bond_xmit_hash_policy": "layer3+4", -// "bond_miimon": 100, -// "mtu": 9100 -// }, -// { -// "id": "bond0.41", -// "name": "bond0.41", -// "type": "vlan", -// "vlan_link": "bond0", -// "vlan_id": 41, -// "mtu": 9100, -// "vlan_mac_address": null -// }, -// { -// "id": "bond0.42", -// "name": "bond0.42", -// "type": "vlan", -// "vlan_link": "bond0", -// "vlan_id": 42, -// "mtu": 9100, -// "vlan_mac_address": null -// }, -// { -// "id": "bond0.44", -// "name": "bond0.44", -// "type": "vlan", -// "vlan_link": "bond0", -// "vlan_id": 44, -// "mtu": 9100, -// "vlan_mac_address": null -// }, -// { -// "id": "bond0.45", -// "name": "bond0.45", -// "type": "vlan", -// "vlan_link": "bond0", -// "vlan_id": 45, -// "mtu": 9100, -// "vlan_mac_address": null -// } -// ], -// "networks": [ -// { -// "id": "oam-ipv6", -// "type": "ipv6", -// "link": "bond0.41", -// "ip_address": "2001:1890:1001:293d::139", -// "routes": [ -// { -// "network": "::/0", -// "netmask": "::/0", -// "gateway": "2001:1890:1001:293d::1" -// } -// ] -// }, -// { -// "id": "oam-ipv4", -// "type": "ipv4", -// "link": "bond0.41", -// "ip_address": "32.68.51.139", -// "netmask": "255.255.255.128", -// "dns_nameservers": [ -// "135.188.34.124", -// "135.38.244.16", -// "135.188.34.84" -// ], -// "routes": [ -// { -// "network": "0.0.0.0", -// "netmask": "0.0.0.0", -// "gateway": "32.68.51.129" -// } -// ] -// }, -// { -// "id": "pxe-ipv6", -// "link": "eno4", -// "type": "ipv6", -// "ip_address": "fd00:900:100:138::11" -// }, -// { -// "id": "pxe-ipv4", -// "link": "eno4", -// "type": "ipv4", -// "ip_address": "172.30.0.11", -// "netmask": "255.255.255.128" -// }, -// { -// "id": "storage-ipv6", -// "link": "bond0.42", -// "type": "ipv6", -// "ip_address": "fd00:900:100:139::15" -// }, -// { -// "id": "storage-ipv4", -// "link": "bond0.42", -// "type": "ipv4", -// "ip_address": "172.31.1.15", -// "netmask": "255.255.255.128" -// }, -// { -// "id": "ksn-ipv6", -// "link": "bond0.44", -// "type": "ipv6", -// "ip_address": "fd00:900:100:13a::11" -// }, -// { -// "id": "ksn-ipv4", -// "link": "bond0.44", -// "type": "ipv4", -// "ip_address": "172.29.0.11", -// "netmask": "255.255.255.128" -// } -// ] -//}` ) // VinoReconciler reconciles a Vino object @@ -212,6 +59,7 @@ type VinoReconciler struct { // +kubebuilder:rbac:groups=airship.airshipit.org,resources=vinoes/status,verbs=get;update;patch // +kubebuilder:rbac:groups="",resources=pods,verbs=list;watch // +kubebuilder:rbac:groups="",resources=nodes,verbs=get;list;watch +// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;patch func (r *VinoReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { logger := logr.FromContext(ctx) @@ -615,10 +463,10 @@ func (r *VinoReconciler) daemonSet(ctx context.Context, vino *vinov1.Vino) (*app return nil, err } - template, exist := cm.Data[DaemonSetTemplateDefaultDataKey] + template, exist := cm.Data[TemplateDefaultKey] if !exist { - logger.Info("malformed template provided data doesn't have key " + DaemonSetTemplateDefaultDataKey) - return nil, fmt.Errorf("malformed template provided data doesn't have key " + DaemonSetTemplateDefaultDataKey) + logger.Info("malformed template provided data doesn't have key " + TemplateDefaultKey) + return nil, fmt.Errorf("malformed template provided data doesn't have key " + TemplateDefaultKey) } ds := &appsv1.DaemonSet{} diff --git a/tools/deployment/test-cr.sh b/tools/deployment/test-cr.sh index 2341785..588333f 100755 --- a/tools/deployment/test-cr.sh +++ b/tools/deployment/test-cr.sh @@ -14,6 +14,7 @@ function vinoDebugInfo () { kubectl apply -f config/samples/vino_cr.yaml kubectl apply -f config/samples/ippool.yaml +kubectl apply -f config/samples/network-template-secret.yaml # Remove logs collection from here, when we will have zuul collect logs job until [[ $(kubectl get vino vino-test-cr 2>/dev/null) ]]; do @@ -43,9 +44,10 @@ if ! kubectl -n vino-system rollout status ds default-vino-test-cr --timeout=10s vinoDebugInfo fi - bmhCount=$(kubectl get baremetalhosts -n vino-system -o name | wc -l) # with this setup set up, exactly 3 BMHs must have been created by VINO controller [[ "$bmhCount" -eq "3" ]] + +kubectl get secret -o yaml -n vino-system default-vino-test-cr-worker