Skip to content

Commit 69e2550

Browse files
committed
feat: ConfigMap image mapping overrides for LLS Distro
Implements a mechanism for the Llama Stack Operator to read and apply LLS Distribution image updates from a ConfigMap, enabling independent patching for security fixes or bug fixes without requiring a new LLS Operator or RHOAI version. - Add RHODS version detection from ClusterServiceVersion - Add ImageMappingOverrides field to LlamaStackDistributionReconciler - Implement parseImageMappingOverrides() to read image-overrides from ConfigMap - Add RBAC permissions for operators.coreos.com/clusterserviceversions - Support symbolic name mapping (e.g., rhdev-2.25) to specific images - Included unit tests The operator now reads image overrides from the 'image-overrides' key in the operator ConfigMap, supporting YAML format with version-to-image mappings. Overrides take precedence over default distribution images and are refreshed on each reconciler initialization. Closes: RHAIENG-1079 Signed-off-by: Derek Higgins <[email protected]>
1 parent c1621ec commit 69e2550

File tree

11 files changed

+903
-188
lines changed

11 files changed

+903
-188
lines changed

README.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,47 @@ Example to create a run.yaml ConfigMap, and a LlamaStackDistribution that refere
104104
kubectl apply -f config/samples/example-with-configmap.yaml
105105
```
106106

107+
## Image Mapping Overrides
108+
109+
The operator supports ConfigMap-driven image updates for LLS Distribution images. This allows independent patching for security fixes or bug fixes without requiring a new operator version.
110+
111+
### Configuration
112+
113+
Create or update the operator ConfigMap with an `image-overrides` key:
114+
115+
```yaml
116+
apiVersion: v1
117+
kind: ConfigMap
118+
metadata:
119+
name: llama-stack-operator-config
120+
namespace: llama-stack-k8s-operator-system
121+
data:
122+
image-overrides: |
123+
rh-dev-2.25: quay.io/rhoai/rhoai-fbc-fragment:rhoai-2.25@sha256:3bc98555
124+
```
125+
126+
### Symbolic Name Format
127+
128+
Use the format `rh-dev-<major>-<minor>` for symbolic names that correspond to RHOAI versions. The operator will automatically detect the current RHOAI version and apply the appropriate override.
129+
130+
### How It Works
131+
132+
1. The operator reads image overrides from the `image-overrides` key in the operator ConfigMap
133+
2. Overrides are parsed as YAML with version-to-image mappings
134+
3. When deploying a LlamaStackDistribution, the operator checks for overrides matching the current RHOAI version
135+
4. If an override exists, it uses the specified image instead of the default distribution image
136+
5. Changes to the ConfigMap automatically trigger reconciliation of all LlamaStackDistribution resources
137+
138+
### Example Usage
139+
140+
To update the LLS Distribution image for RHOAI 2.25:
141+
142+
```bash
143+
kubectl patch configmap llama-stack-operator-config -n llama-stack-k8s-operator-system --type merge -p '{"data":{"image-overrides":"rh-dev-2.25: quay.io/opendatahub/llama-stack:latest"}}'
144+
```
145+
146+
This will cause all LlamaStackDistribution resources using the `rh-dev` name to restart with the new image while using RHOAI version 2.25.Z, once RHOAI is upgraded to another version then the distribution will revert back to the default for that version.
147+
107148
## Developer Guide
108149

109150
### Prerequisites

config/crd/bases/llamastack.io_llamastackdistributions.yaml

Lines changed: 197 additions & 33 deletions
Large diffs are not rendered by default.

config/rbac/role.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,14 @@ rules:
8787
- patch
8888
- update
8989
- watch
90+
- apiGroups:
91+
- operators.coreos.com
92+
resources:
93+
- clusterserviceversions
94+
verbs:
95+
- get
96+
- list
97+
- watch
9098
- apiGroups:
9199
- rbac.authorization.k8s.io
92100
resources:

controllers/kubebuilder_rbac.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,6 @@ package controllers
2727

2828
// NetworkPolicy permissions - controller creates and manages network policies
2929
//+kubebuilder:rbac:groups=networking.k8s.io,resources=networkpolicies,verbs=get;list;watch;create;update;patch;delete
30+
31+
// ClusterServiceVersion permissions - controller reads CSV to get RHOAI version
32+
//+kubebuilder:rbac:groups=operators.coreos.com,resources=clusterserviceversions,verbs=get;list;watch

controllers/llamastackdistribution_controller.go

Lines changed: 68 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import (
3434
"github.com/llamastack/llama-stack-k8s-operator/pkg/cluster"
3535
"github.com/llamastack/llama-stack-k8s-operator/pkg/deploy"
3636
"github.com/llamastack/llama-stack-k8s-operator/pkg/featureflags"
37+
olmv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
3738
"gopkg.in/yaml.v3"
3839
appsv1 "k8s.io/api/apps/v1"
3940
corev1 "k8s.io/api/core/v1"
@@ -47,6 +48,7 @@ import (
4748
ctrl "sigs.k8s.io/controller-runtime"
4849
"sigs.k8s.io/controller-runtime/pkg/builder"
4950
"sigs.k8s.io/controller-runtime/pkg/client"
51+
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
5052
"sigs.k8s.io/controller-runtime/pkg/event"
5153
"sigs.k8s.io/controller-runtime/pkg/handler"
5254
"sigs.k8s.io/controller-runtime/pkg/log"
@@ -89,6 +91,10 @@ type LlamaStackDistributionReconciler struct {
8991
Scheme *runtime.Scheme
9092
// Feature flags
9193
EnableNetworkPolicy bool
94+
// RHODS version
95+
RHODSVersion string
96+
// Image mapping overrides
97+
ImageMappingOverrides map[string]string
9298
// Cluster info
9399
ClusterInfo *cluster.ClusterInfo
94100
httpClient *http.Client
@@ -474,6 +480,7 @@ func (r *LlamaStackDistributionReconciler) configMapUpdatePredicate(e event.Upda
474480
} else {
475481
r.EnableNetworkPolicy = EnableNetworkPolicy
476482
}
483+
r.ImageMappingOverrides = ParseImageMappingOverrides(newConfigMap.Data)
477484
return true
478485
}
479486

@@ -1474,28 +1481,79 @@ func NewLlamaStackDistributionReconciler(ctx context.Context, client client.Clie
14741481
}
14751482
}
14761483

1484+
// get the version of RHODS/RHOAI from the ClusterServiceVersion in the current namespace
1485+
var rhodsVersion string
1486+
csvlist := &olmv1alpha1.ClusterServiceVersionList{}
1487+
listOpts := []ctrlclient.ListOption{
1488+
ctrlclient.InNamespace(operatorNamespace),
1489+
}
1490+
err = client.List(ctx, csvlist, listOpts...)
1491+
if err != nil {
1492+
fmt.Printf("failed to list ClusterServiceVersions: %v\n", err)
1493+
}
1494+
for _, csv := range csvlist.Items {
1495+
if strings.HasPrefix(csv.Name, "rhods-operator") {
1496+
rhodsVersion = csv.Spec.Version.String()
1497+
break
1498+
}
1499+
}
1500+
if rhodsVersion == "" {
1501+
fmt.Printf("failed to find RHODS version from ClusterServiceVersion\n")
1502+
}
1503+
14771504
// Parse feature flags from ConfigMap
14781505
enableNetworkPolicy, err := parseFeatureFlags(configMap.Data)
14791506
if err != nil {
14801507
return nil, fmt.Errorf("failed to parse feature flags: %w", err)
14811508
}
1509+
1510+
// Parse image mapping overrides from ConfigMap
1511+
imageMappingOverrides := ParseImageMappingOverrides(configMap.Data)
1512+
14821513
return &LlamaStackDistributionReconciler{
1483-
Client: client,
1484-
Scheme: scheme,
1485-
EnableNetworkPolicy: enableNetworkPolicy,
1486-
ClusterInfo: clusterInfo,
1487-
httpClient: &http.Client{Timeout: 5 * time.Second},
1514+
Client: client,
1515+
Scheme: scheme,
1516+
EnableNetworkPolicy: enableNetworkPolicy,
1517+
RHODSVersion: rhodsVersion,
1518+
ImageMappingOverrides: imageMappingOverrides,
1519+
ClusterInfo: clusterInfo,
1520+
httpClient: &http.Client{Timeout: 5 * time.Second},
14881521
}, nil
14891522
}
14901523

1524+
func ParseImageMappingOverrides(configMapData map[string]string) map[string]string {
1525+
imageMappingOverrides := make(map[string]string)
1526+
1527+
// Look for the image-overrides key in the ConfigMap data
1528+
if overridesYAML, exists := configMapData["image-overrides"]; exists {
1529+
// Parse the YAML content
1530+
var overrides map[string]string
1531+
if err := yaml.Unmarshal([]byte(overridesYAML), &overrides); err != nil {
1532+
// Log error but continue with empty overrides
1533+
fmt.Printf("failed to parse image-overrides YAML: %v\n", err)
1534+
return imageMappingOverrides
1535+
}
1536+
1537+
// Copy the parsed overrides to our result map
1538+
for version, image := range overrides {
1539+
fmt.Printf("image-mapping-override: %s: %s\n", version, image)
1540+
imageMappingOverrides[version] = image
1541+
}
1542+
}
1543+
1544+
return imageMappingOverrides
1545+
}
1546+
14911547
// NewTestReconciler creates a reconciler for testing, allowing injection of a custom http client and feature flags.
14921548
func NewTestReconciler(client client.Client, scheme *runtime.Scheme, clusterInfo *cluster.ClusterInfo,
14931549
httpClient *http.Client, enableNetworkPolicy bool) *LlamaStackDistributionReconciler {
14941550
return &LlamaStackDistributionReconciler{
1495-
Client: client,
1496-
Scheme: scheme,
1497-
ClusterInfo: clusterInfo,
1498-
httpClient: httpClient,
1499-
EnableNetworkPolicy: enableNetworkPolicy,
1551+
Client: client,
1552+
Scheme: scheme,
1553+
ClusterInfo: clusterInfo,
1554+
httpClient: httpClient,
1555+
EnableNetworkPolicy: enableNetworkPolicy,
1556+
RHODSVersion: "",
1557+
ImageMappingOverrides: make(map[string]string),
15001558
}
15011559
}

0 commit comments

Comments
 (0)