Skip to content

Commit 802869b

Browse files
authored
Merge pull request #417 from spotlesstofu/feature-confidential
Add feature confidential
2 parents 0102960 + 8a92c65 commit 802869b

File tree

7 files changed

+190
-93
lines changed

7 files changed

+190
-93
lines changed

Dockerfile

-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ COPY main.go main.go
1515
COPY api api/
1616
COPY config config/
1717
COPY controllers controllers/
18-
COPY internal internal/
1918

2019
RUN go mod download
2120
# needed for docker build but not for local builds

bundle-custom.Dockerfile

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Use OpenShift golang builder image
2-
FROM registry.ci.openshift.org/ocp/builder:rhel-8-golang-1.18-openshift-4.11 AS builder
2+
FROM registry.ci.openshift.org/ocp/builder:rhel-9-golang-1.21-openshift-4.16 AS builder
33

44
WORKDIR /workspace
55

@@ -10,7 +10,6 @@ COPY go.sum go.sum
1010
COPY api api/
1111
COPY config config/
1212
COPY controllers controllers/
13-
COPY internal internal/
1413

1514
RUN go mod download
1615
# needed for docker build but not for local builds

controllers/confidential_handler.go

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package controllers
2+
3+
import (
4+
k8serrors "k8s.io/apimachinery/pkg/api/errors"
5+
)
6+
7+
// When the feature is enabled, handleFeatureConfidential sets config maps to confidential values.
8+
//
9+
// Changes the ImageConfigMap, so that the image creation job will create a confidential image.
10+
// This will happen at the first reconciliation loop, before the image creation job starts.
11+
//
12+
// Changes the peer pods configMap to enable confidential.
13+
// This will happen likely after several reconciliation loops, because it has prerequisites:
14+
//
15+
// - Peer pods must be enabled in the KataConfig.
16+
// - The peer pods config map must exist.
17+
//
18+
// When the feature is disabled, handleFeatureConfidential resets the config maps to non-confidential values.
19+
func (r *KataConfigOpenShiftReconciler) handleFeatureConfidential(state FeatureGateState) error {
20+
21+
// ImageConfigMap
22+
23+
if err := InitializeImageGenerator(r.Client); err != nil {
24+
return err
25+
}
26+
ig := GetImageGenerator()
27+
28+
if ig.provider == unsupportedCloudProvider {
29+
r.Log.Info("unsupported cloud provider, skipping confidential image configuration")
30+
} else {
31+
if ig.isImageIDSet() {
32+
r.Log.Info("Image ID is already set, skipping confidential image configuration")
33+
} else {
34+
if state == Enabled {
35+
// Create ImageConfigMap, if it doesn't exist already.
36+
if err := ig.createImageConfigMapFromFile(); err != nil {
37+
return err
38+
}
39+
40+
// Patch ImageConfigMap.
41+
imageConfigMapData := map[string]string{"CONFIDENTIAL_COMPUTE_ENABLED": "yes"}
42+
if err := updateConfigMap(r.Client, r.Log, ig.getImageConfigMapName(), OperatorNamespace, imageConfigMapData); err != nil {
43+
return err
44+
}
45+
} else {
46+
// Patch ImageConfigMap.
47+
imageConfigMapData := map[string]string{"CONFIDENTIAL_COMPUTE_ENABLED": "no"}
48+
if err := updateConfigMap(r.Client, r.Log, ig.getImageConfigMapName(), OperatorNamespace, imageConfigMapData); err != nil {
49+
if k8serrors.IsNotFound(err) {
50+
// Nothing to do, feature is disabled and configMap doesn't exist.
51+
} else {
52+
return err
53+
}
54+
}
55+
}
56+
}
57+
}
58+
59+
// peer pods config
60+
61+
// Patch peer pods configMap, if it exists.
62+
var peerpodsCMData map[string]string
63+
if state == Enabled {
64+
peerpodsCMData = map[string]string{"DISABLECVM": "false"}
65+
} else {
66+
peerpodsCMData = map[string]string{"DISABLECVM": "true"}
67+
}
68+
if err := updateConfigMap(r.Client, r.Log, peerpodsCMName, OperatorNamespace, peerpodsCMData); err != nil {
69+
if k8serrors.IsNotFound(err) {
70+
// When feature is Enabled: ConfigMap doesn't exist yet, will try again at the next reconcile run.
71+
// Else: Nothing to do, feature is disabled and configMap doesn't exist.
72+
} else {
73+
return err
74+
}
75+
}
76+
77+
return nil
78+
}

controllers/fg_handler.go

+72-24
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,100 @@
11
package controllers
22

33
import (
4-
"github.com/openshift/sandboxed-containers-operator/internal/featuregates"
4+
"context"
5+
6+
corev1 "k8s.io/api/core/v1"
7+
k8serrors "k8s.io/apimachinery/pkg/api/errors"
8+
"k8s.io/apimachinery/pkg/types"
9+
)
10+
11+
const (
12+
FgConfigMapName = "osc-feature-gates"
13+
ConfidentialFeatureGate = "confidential"
514
)
615

16+
var DefaultFeatureGates = map[string]bool{
17+
ConfidentialFeatureGate: false,
18+
}
19+
20+
type FeatureGateStatus struct {
21+
FeatureGates map[string]bool
22+
}
23+
724
// Create enum to represent the state of the feature gates
25+
// While today we just have two states, we retain the flexibility in case we want to introduce some additional states.
826
type FeatureGateState int
927

1028
const (
1129
Enabled FeatureGateState = iota
1230
Disabled
1331
)
1432

33+
// This method returns a new FeatureGateStatus object
34+
// that contains the status of the feature gates
35+
// defined in the ConfigMap in the namespace
36+
// Return default values if the ConfigMap is not found.
37+
// Return values from the ConfigMap if the ConfigMap is not found. Use default values for missing entries in the ConfigMap.
38+
// Return an error for any other reason, such as an API error.
39+
func (r *KataConfigOpenShiftReconciler) NewFeatureGateStatus() (*FeatureGateStatus, error) {
40+
fgStatus := &FeatureGateStatus{
41+
FeatureGates: make(map[string]bool),
42+
}
43+
44+
cfgMap := &corev1.ConfigMap{}
45+
err := r.Client.Get(context.TODO(), types.NamespacedName{Name: FgConfigMapName,
46+
Namespace: OperatorNamespace}, cfgMap)
47+
if err == nil {
48+
for feature, value := range cfgMap.Data {
49+
fgStatus.FeatureGates[feature] = value == "true"
50+
}
51+
}
52+
53+
// Add default values for missing feature gates
54+
for feature, defaultValue := range DefaultFeatureGates {
55+
if _, exists := fgStatus.FeatureGates[feature]; !exists {
56+
fgStatus.FeatureGates[feature] = defaultValue
57+
}
58+
}
59+
60+
if k8serrors.IsNotFound(err) {
61+
return fgStatus, nil
62+
} else {
63+
return fgStatus, err
64+
}
65+
}
66+
67+
func IsEnabled(fgStatus *FeatureGateStatus, feature string) bool {
68+
return fgStatus.FeatureGates[feature]
69+
}
70+
1571
// Function to handle the feature gates
1672
func (r *KataConfigOpenShiftReconciler) processFeatureGates() error {
1773

18-
fgStatus, err := featuregates.NewFeatureGateStatus(r.Client)
74+
fgStatus, err := r.NewFeatureGateStatus()
1975
if err != nil {
2076
r.Log.Info("There were errors in getting feature gate status.", "err", err)
2177
return err
2278
}
2379

2480
// Check which feature gates are enabled in the FG ConfigMap and
2581
// perform the necessary actions
26-
// The feature gates are defined in internal/featuregates/featuregates.go
27-
// and are fetched from the ConfigMap in the namespace
28-
// Eg. TimeTravelFeatureGate
29-
30-
if featuregates.IsEnabled(fgStatus, featuregates.TimeTravelFeatureGate) {
31-
r.Log.Info("Feature gate is enabled", "featuregate", featuregates.TimeTravelFeatureGate)
32-
// Perform the necessary actions
33-
r.handleTimeTravelFeature(Enabled)
34-
} else {
35-
r.Log.Info("Feature gate is disabled", "featuregate", featuregates.TimeTravelFeatureGate)
36-
// Perform the necessary actions
37-
r.handleTimeTravelFeature(Disabled)
82+
if r.kataConfig.Spec.EnablePeerPods {
83+
if IsEnabled(fgStatus, ConfidentialFeatureGate) {
84+
r.Log.Info("Feature gate is enabled", "featuregate", ConfidentialFeatureGate)
85+
// Perform the necessary actions
86+
if err := r.handleFeatureConfidential(Enabled); err != nil {
87+
return err
88+
}
89+
} else {
90+
r.Log.Info("Feature gate is disabled", "featuregate", ConfidentialFeatureGate)
91+
// Perform the necessary actions
92+
if err := r.handleFeatureConfidential(Disabled); err != nil {
93+
return err
94+
}
95+
}
3896
}
3997

4098
return err
4199

42100
}
43-
44-
// Function to handle the TimeTravel feature gate
45-
func (r *KataConfigOpenShiftReconciler) handleTimeTravelFeature(state FeatureGateState) {
46-
// Perform the necessary actions for the TimeTravel feature gate
47-
if state == Enabled {
48-
r.Log.Info("Starting TimeTravel")
49-
} else {
50-
r.Log.Info("Stopping TimeTravel")
51-
}
52-
}

controllers/image_generator.go

+7-3
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,10 @@ func checkKeysPresentAndNotEmpty(data interface{}, keys []string) bool {
631631
return true
632632
}
633633

634+
func (r *ImageGenerator) getImageConfigMapName() string {
635+
return r.provider + "-podvm-image-cm"
636+
}
637+
634638
// Function to create ConfigMap from a YAML file based on cloud provider
635639
// azure-podvm-image-cm.yaml for Azure
636640
// aws-podvm-image-cm.yaml for AWS
@@ -645,14 +649,14 @@ func (r *ImageGenerator) createImageConfigMapFromFile() error {
645649
// If it doesn't exist, create the ConfigMap
646650

647651
if err := r.client.Get(context.TODO(), types.NamespacedName{
648-
Name: r.provider + "-podvm-image-cm",
652+
Name: r.getImageConfigMapName(),
649653
Namespace: OperatorNamespace,
650654
}, &corev1.ConfigMap{}); err == nil {
651655
igLogger.Info("ConfigMap already exists", "name", r.provider+"-podvm-image-cm")
652656
return nil
653657
}
654658

655-
filename := r.provider + "-podvm-image-cm.yaml"
659+
filename := r.getImageConfigMapName() + ".yaml"
656660
configMapYamlFile := filepath.Join(peerpodsImageJobsPathLocation, filename)
657661
yamlData, err := readYamlFile(configMapYamlFile)
658662
if err != nil {
@@ -693,7 +697,7 @@ func (r *ImageGenerator) updateImageConfigMap() error {
693697
// If it doesn't exist, return an error
694698
// If it exists, update the ConfigMap
695699

696-
cmName := r.provider + "-podvm-image-cm"
700+
cmName := r.getImageConfigMapName()
697701
cm := &corev1.ConfigMap{}
698702
if err := r.client.Get(context.TODO(), types.NamespacedName{
699703
Name: cmName,

controllers/utils.go

+32
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"time"
1010

1111
yaml "github.com/ghodss/yaml"
12+
"github.com/go-logr/logr"
1213
configv1 "github.com/openshift/api/config/v1"
1314
ccov1 "github.com/openshift/cloud-credential-operator/pkg/apis/cloudcredential/v1"
1415
mcfgv1 "github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1"
@@ -192,3 +193,34 @@ func getClusterID(c client.Client) (string, error) {
192193
// Return first 8 characters of the cluster id
193194
return string(clusterVersion.Spec.ClusterID[:8]), nil
194195
}
196+
197+
func updateConfigMap(client client.Client, logger logr.Logger, cmName string, namespace string, newData map[string]string) error {
198+
// Get current configMap.
199+
configMap := &corev1.ConfigMap{}
200+
if err := client.Get(context.TODO(), types.NamespacedName{
201+
Name: cmName,
202+
Namespace: namespace,
203+
}, configMap); err != nil {
204+
return err
205+
}
206+
207+
update := false
208+
209+
// Loop over each new value
210+
// Update the value if it's different.
211+
// Log the change.
212+
for key, newValue := range newData {
213+
if configMap.Data[key] != newValue {
214+
logger.Info("updateConfigMap", "namespace", namespace, "name", cmName, "key", key, "value", newValue)
215+
configMap.Data[key] = newValue
216+
update = true
217+
}
218+
}
219+
220+
if update {
221+
// Update the configMap on Kubernetes.
222+
return client.Update(context.TODO(), configMap)
223+
} else {
224+
return nil
225+
}
226+
}

internal/featuregates/featuregates.go

-63
This file was deleted.

0 commit comments

Comments
 (0)