Skip to content

Commit b9d4c93

Browse files
author
Brendan Shephard
committed
Use OCP registries.conf for disconnected deployments
This change adds functionality to check for ImageContentSourcePolicy in the OCP cluster. If we find it, we infer from it's existance that we are running in a disconnected OCP deployment. If this is indeed the case, we will pull the registries.conf file from the MachineConfig and use this for our dataplane nodes configured via edpm_ansible. Signed-off-by: Brendan Shephard <bshephar@redhat.com> Update RBAC for ICSP and MachineConfig This change allows the OpenStackDataPlaneNodeSet controller to get and list ImageContentSourcePolicy and MachineConfig CR's from the cluster. This facilitates integration of the dataplane with the OCP registries.conf configuration to ensure consistency. Signed-off-by: Brendan Shephard <bshephar@redhat.com> Return err if no prefix Signed-off-by: Brendan Shephard <bshephar@redhat.com>
1 parent 357a9af commit b9d4c93

6 files changed

Lines changed: 189 additions & 15 deletions

File tree

apis/go.mod

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,15 @@ require (
3535
go.uber.org/zap v1.27.0 // indirect
3636
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
3737
golang.org/x/tools v0.24.0 // indirect
38-
k8s.io/api v0.29.9
3938
k8s.io/apimachinery v0.29.9
4039
k8s.io/client-go v0.29.9
4140
sigs.k8s.io/controller-runtime v0.17.6
4241
)
4342

44-
require k8s.io/utils v0.0.0-20240711033017-18e509b52bc8
43+
require (
44+
k8s.io/api v0.29.9
45+
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8
46+
)
4547

4648
require (
4749
github.com/beorn7/perks v1.0.1 // indirect

config/rbac/role.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,13 @@ rules:
499499
- patch
500500
- update
501501
- watch
502+
- apiGroups:
503+
- machineconfiguration.openshift.io
504+
resources:
505+
- machineconfig
506+
verbs:
507+
- get
508+
- list
502509
- apiGroups:
503510
- manila.openstack.org
504511
resources:
@@ -647,6 +654,13 @@ rules:
647654
- patch
648655
- update
649656
- watch
657+
- apiGroups:
658+
- operator.openshift.io
659+
resources:
660+
- imagecontentsourcepolicy
661+
verbs:
662+
- get
663+
- list
650664
- apiGroups:
651665
- ovn.openstack.org
652666
resources:

controllers/dataplane/openstackdataplanenodeset_controller.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ func (r *OpenStackDataPlaneNodeSetReconciler) GetLogger(ctx context.Context) log
120120
// +kubebuilder:rbac:groups="image.openshift.io",resources=imagetags,verbs=get;list;watch
121121
// +kubebuilder:rbac:groups="image.openshift.io",resources=imagestreamtags,verbs=get;list;watch
122122

123+
// RBAC for ImageContentSourcePolicy and MachineConfig
124+
// +kubebuilder:rbac:groups="operator.openshift.io",resources=imagecontentsourcepolicy,verbs=get;list
125+
// +kubebuilder:rbac:groups="machineconfiguration.openshift.io",resources=machineconfig,verbs=get;list
126+
123127
// Reconcile is part of the main kubernetes reconciliation loop which aims to
124128
// move the current state of the cluster closer to the desired state.
125129
// TODO(user): Modify the Reconcile function to compare the state specified by
@@ -565,7 +569,8 @@ func checkDeployment(ctx context.Context, helper *helper.Helper,
565569

566570
// SetupWithManager sets up the controller with the Manager.
567571
func (r *OpenStackDataPlaneNodeSetReconciler) SetupWithManager(
568-
ctx context.Context, mgr ctrl.Manager) error {
572+
ctx context.Context, mgr ctrl.Manager,
573+
) error {
569574
// index for ConfigMaps listed on ansibleVarsFrom
570575
if err := mgr.GetFieldIndexer().IndexField(ctx,
571576
&dataplanev1.OpenStackDataPlaneNodeSet{}, "spec.ansibleVarsFrom.ansible.configMaps",

main.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ import (
7777
dataplanev1 "github.com/openstack-k8s-operators/openstack-operator/apis/dataplane/v1beta1"
7878

7979
ocp_configv1 "github.com/openshift/api/config/v1"
80+
machineconfig "github.com/openshift/api/machineconfiguration/v1"
81+
ocp_image "github.com/openshift/api/operator/v1alpha1"
8082
clientcontrollers "github.com/openstack-k8s-operators/openstack-operator/controllers/client"
8183
corecontrollers "github.com/openstack-k8s-operators/openstack-operator/controllers/core"
8284
dataplanecontrollers "github.com/openstack-k8s-operators/openstack-operator/controllers/dataplane"
@@ -120,6 +122,8 @@ func init() {
120122
utilruntime.Must(certmgrv1.AddToScheme(scheme))
121123
utilruntime.Must(barbicanv1.AddToScheme(scheme))
122124
utilruntime.Must(ocp_configv1.AddToScheme(scheme))
125+
utilruntime.Must(ocp_image.AddToScheme(scheme))
126+
utilruntime.Must(machineconfig.AddToScheme(scheme))
123127
// +kubebuilder:scaffold:scheme
124128
}
125129

pkg/dataplane/inventory.go

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@ import (
3838

3939
// getAnsibleVarsFrom gets ansible vars from ConfigMap/Secret
4040
func getAnsibleVarsFrom(ctx context.Context, helper *helper.Helper, namespace string, ansible *dataplanev1.AnsibleOpts) (map[string]interface{}, error) {
41-
42-
var result = make(map[string]interface{})
41+
result := make(map[string]interface{})
4342
var value interface{}
4443

4544
for _, dataSource := range ansible.AnsibleVarsFrom {
@@ -90,7 +89,8 @@ func GenerateNodeSetInventory(ctx context.Context, helper *helper.Helper,
9089
instance *dataplanev1.OpenStackDataPlaneNodeSet,
9190
allIPSets map[string]infranetworkv1.IPSet, dnsAddresses []string,
9291
containerImages openstackv1.ContainerImages,
93-
netServiceNetMap map[string]string) (string, error) {
92+
netServiceNetMap map[string]string,
93+
) (string, error) {
9494
inventory := ansible.MakeInventory()
9595
nodeSetGroup := inventory.AddGroup(instance.Name)
9696
groupVars, err := getAnsibleVarsFrom(ctx, helper, instance.Namespace, &instance.Spec.NodeTemplate.Ansible)
@@ -111,6 +111,23 @@ func GenerateNodeSetInventory(ctx context.Context, helper *helper.Helper,
111111
// add the NodeSet name variable
112112
nodeSetGroup.Vars["edpm_nodeset_name"] = instance.Name
113113

114+
isDisconnected, err := util.IsDisconnectedOCP(ctx, helper)
115+
if err != nil {
116+
return "", err
117+
}
118+
119+
if isDisconnected {
120+
registryConfig, err := util.GetMCRegistryConf(ctx, helper)
121+
if err != nil {
122+
return "", err
123+
}
124+
helper.GetLogger().Info("disconnected registry was identified via the ImageContentSourcePolicy. Using OCP registry.")
125+
126+
// Use OCP registries.conf for disconnected deployments
127+
nodeSetGroup.Vars["edpm_podman_registries_conf"] = registryConfig
128+
nodeSetGroup.Vars["edpm_podman_disconnected_ocp"] = isDisconnected
129+
}
130+
114131
// add TLS ansible variable
115132
nodeSetGroup.Vars["edpm_tls_certs_enabled"] = instance.Spec.TLSEnabled
116133
if instance.Spec.Tags != nil {
@@ -205,7 +222,8 @@ func GenerateNodeSetInventory(ctx context.Context, helper *helper.Helper,
205222
// populateInventoryFromIPAM populates inventory from IPAM
206223
func populateInventoryFromIPAM(
207224
ipSet *infranetworkv1.IPSet, host ansible.Host,
208-
dnsAddresses []string, hostName string) {
225+
dnsAddresses []string, hostName string,
226+
) {
209227
var dnsSearchDomains []string
210228
for _, res := range ipSet.Status.Reservation {
211229
// Build the vars for ips/routes etc
@@ -243,8 +261,8 @@ func populateInventoryFromIPAM(
243261
// set group ansible vars from NodeTemplate
244262
func resolveGroupAnsibleVars(template *dataplanev1.NodeTemplate, group *ansible.Group,
245263
containerImages openstackv1.ContainerImages,
246-
netServiceNetMap map[string]string) error {
247-
264+
netServiceNetMap map[string]string,
265+
) error {
248266
if template.Ansible.AnsibleUser != "" {
249267
group.Vars["ansible_user"] = template.Ansible.AnsibleUser
250268
}
@@ -320,8 +338,8 @@ func resolveGroupAnsibleVars(template *dataplanev1.NodeTemplate, group *ansible.
320338

321339
// set host ansible vars from NodeSection
322340
func resolveHostAnsibleVars(node *dataplanev1.NodeSection,
323-
host *ansible.Host, netServiceNetMap map[string]string) error {
324-
341+
host *ansible.Host, netServiceNetMap map[string]string,
342+
) error {
325343
if node.Ansible.AnsibleUser != "" {
326344
host.Vars["ansible_user"] = node.Ansible.AnsibleUser
327345
}
@@ -344,13 +362,12 @@ func resolveHostAnsibleVars(node *dataplanev1.NodeSection,
344362
host.Vars["network_servicenet_map"] = serviceNetMap
345363
}
346364
return nil
347-
348365
}
349366

350367
// unmarshal raw strings into an ansible vars dictionary
351368
func unmarshalAnsibleVars(ansibleVars map[string]json.RawMessage,
352-
parsedVars map[string]interface{}) error {
353-
369+
parsedVars map[string]interface{},
370+
) error {
354371
for key, val := range ansibleVars {
355372
var v interface{}
356373
err := yaml.Unmarshal(val, &v)
@@ -363,7 +380,8 @@ func unmarshalAnsibleVars(ansibleVars map[string]json.RawMessage,
363380
}
364381

365382
func buildNetworkVars(networks []infranetworkv1.IPSetNetwork,
366-
netServiceNetMap map[string]string) ([]string, map[string]string) {
383+
netServiceNetMap map[string]string,
384+
) ([]string, map[string]string) {
367385
serviceNetMap := make(map[string]string)
368386
var nets []string
369387
for _, network := range networks {
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package util
2+
3+
import (
4+
"context"
5+
"encoding/base64"
6+
"encoding/json"
7+
"fmt"
8+
"strings"
9+
10+
mc "github.com/openshift/api/machineconfiguration/v1"
11+
ocpimage "github.com/openshift/api/operator/v1alpha1"
12+
"github.com/openstack-k8s-operators/lib-common/modules/common/helper"
13+
"sigs.k8s.io/controller-runtime/pkg/client"
14+
15+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
16+
"k8s.io/apimachinery/pkg/types"
17+
)
18+
19+
// machineConfigIgnition - holds the relevant parts of the ignition file that we need to create
20+
// the registries.conf on the dataplane nodes.
21+
type machineConfigIgnition struct {
22+
Ignition struct {
23+
Version string `json:"version"`
24+
} `json:"ignition"`
25+
Storage struct {
26+
Files []struct {
27+
Contents struct {
28+
Compression string `json:"compression,omitempty"`
29+
Source string `json:"source"`
30+
} `json:"contents"`
31+
Mode int `json:"mode,omitempty"`
32+
Overwrite bool `json:"overwrite,omitempty"`
33+
Path string `json:"path"`
34+
} `json:"files"`
35+
} `json:"storage"`
36+
}
37+
38+
// IsDisconnectedOCP - Will retrieve a ImageContentSourcePolicyList. If the list is not
39+
// empty, we can infer that the OCP cluster is a disconnected deployment.
40+
func IsDisconnectedOCP(ctx context.Context, helper *helper.Helper) (bool, error) {
41+
icspList := ocpimage.ImageContentSourcePolicyList{}
42+
43+
listOpts := []client.ListOption{}
44+
err := helper.GetClient().List(ctx, &icspList, listOpts...)
45+
if err != nil {
46+
return false, err
47+
}
48+
49+
if len(icspList.Items) != 0 {
50+
return true, nil
51+
}
52+
53+
return false, nil
54+
}
55+
56+
// GetMCRegistryConf - will unmarshal the MachineConfig ignition file the machineConfigIgnition object.
57+
// This is then parsed and the base64 decoded string is returned.
58+
func GetMCRegistryConf(ctx context.Context, helper *helper.Helper) (string, error) {
59+
var registriesConf string
60+
61+
masterMachineConfig, err := getMachineConfig(ctx, helper)
62+
if err != nil {
63+
return registriesConf, err
64+
}
65+
66+
config := machineConfigIgnition{}
67+
registriesConf, err = config.formatRegistriesConfString(&masterMachineConfig)
68+
if err != nil {
69+
return registriesConf, err
70+
}
71+
72+
return registriesConf, nil
73+
}
74+
75+
func (mci *machineConfigIgnition) removePrefixFromB64String() (string, error) {
76+
const b64Prefix string = "data:text/plain;charset=utf-8;base64,"
77+
if strings.HasPrefix(mci.Storage.Files[0].Contents.Source, b64Prefix) {
78+
return mci.Storage.Files[0].Contents.Source[len(b64Prefix):], nil
79+
}
80+
81+
return "", fmt.Errorf("no b64prefix found in MachineConfig")
82+
}
83+
84+
func (mci *machineConfigIgnition) formatRegistriesConfString(machineConfig *mc.MachineConfig) (string, error) {
85+
var (
86+
err error
87+
rawConfigString string
88+
configString []byte
89+
)
90+
91+
err = json.Unmarshal([]byte(machineConfig.Spec.Config.Raw), &mci)
92+
if err != nil {
93+
return "", err
94+
}
95+
96+
rawConfigString, err = mci.removePrefixFromB64String()
97+
if err != nil {
98+
return "", err
99+
}
100+
configString, err = base64.StdEncoding.DecodeString(rawConfigString)
101+
if err != nil {
102+
return "", err
103+
}
104+
105+
return string(configString), nil
106+
}
107+
108+
func masterMachineConfigBuilder(machineConfigRegistries string) mc.MachineConfig {
109+
return mc.MachineConfig{
110+
ObjectMeta: metav1.ObjectMeta{
111+
Name: machineConfigRegistries,
112+
Namespace: "",
113+
},
114+
}
115+
}
116+
117+
func getMachineConfig(ctx context.Context, helper *helper.Helper) (mc.MachineConfig, error) {
118+
const machineConfigRegistries string = "99-master-generated-registries"
119+
120+
masterMachineConfig := masterMachineConfigBuilder(machineConfigRegistries)
121+
122+
err := helper.GetClient().Get(ctx,
123+
types.NamespacedName{
124+
Name: masterMachineConfig.Name, Namespace: masterMachineConfig.Namespace,
125+
}, &masterMachineConfig)
126+
if err != nil {
127+
return masterMachineConfig, err
128+
}
129+
130+
return masterMachineConfig, nil
131+
}

0 commit comments

Comments
 (0)