Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 7205919

Browse files
committedSep 8, 2021
Working configmap
1 parent d983ff3 commit 7205919

12 files changed

+864
-10
lines changed
 

‎api/v1alpha1/bundle_types.go

+126
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/*
2+
Copyright 2021 RedHatInsights.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package v1alpha1
18+
19+
import (
20+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
21+
)
22+
23+
type BundlePermissionArgs []string
24+
25+
type BundlePermission struct {
26+
Method string `json:"method"`
27+
Args []BundlePermissionArgs `json:"args"`
28+
}
29+
30+
type BundleNavItem struct {
31+
Title string `json:"title"`
32+
GroupID string `json:"groupId,omitempty"`
33+
NavItems []LeafBundleNavItem `json:"navItems,omitempty"`
34+
AppId string `json:"appId,omitempty"`
35+
Href string `json:"href,omitempty"`
36+
Product string `json:"product,omitempty"`
37+
IsExternal bool `json:"isExternal,omitempty"`
38+
Filterable bool `json:"filterable,omitempty"`
39+
Permissions []BundlePermission `json:"permissions,omitempty"`
40+
Routes []LeafBundleNavItem `json:"routes,omitempty"`
41+
}
42+
43+
type LeafBundleNavItem struct {
44+
Title string `json:"title"`
45+
GroupID string `json:"groupId,omitempty"`
46+
AppId string `json:"appId,omitempty"`
47+
Href string `json:"href,omitempty"`
48+
Product string `json:"product,omitempty"`
49+
IsExternal bool `json:"isExternal,omitempty"`
50+
Filterable bool `json:"filterable,omitempty"`
51+
Permissions []BundlePermission `json:"permissions,omitempty"`
52+
}
53+
54+
type ComputedBundle struct {
55+
ID string `json:"id"`
56+
Title string `json:"title"`
57+
NavItems []BundleNavItem `json:"navItems"`
58+
}
59+
60+
type ExtraNavItem struct {
61+
Name string `json:"name"`
62+
NavItem BundleNavItem `json:"navItem"`
63+
}
64+
65+
// BundleSpec defines the desired state of Bundle
66+
type BundleSpec struct {
67+
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
68+
// Important: Run "make" to regenerate code after modifying this file
69+
70+
// Foo is an example field of Bundle. Edit Bundle_types.go to remove/update
71+
ID string `json:"id"`
72+
Title string `json:"title"`
73+
AppList []string `json:"appList"`
74+
EnvName string `json:"envName"`
75+
ExtraNavItems []ExtraNavItem `json:"extraNavItems,omitempty"`
76+
}
77+
78+
// BundleStatus defines the observed state of Bundle
79+
type BundleStatus struct {
80+
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
81+
// Important: Run "make" to regenerate code after modifying this file
82+
}
83+
84+
//+kubebuilder:object:root=true
85+
//+kubebuilder:subresource:status
86+
87+
// Bundle is the Schema for the Bundles API
88+
type Bundle struct {
89+
metav1.TypeMeta `json:",inline"`
90+
metav1.ObjectMeta `json:"metadata,omitempty"`
91+
92+
Spec BundleSpec `json:"spec,omitempty"`
93+
Status BundleStatus `json:"status,omitempty"`
94+
}
95+
96+
//+kubebuilder:object:root=true
97+
98+
// BundleList contains a list of Bundle
99+
type BundleList struct {
100+
metav1.TypeMeta `json:",inline"`
101+
metav1.ListMeta `json:"metadata,omitempty"`
102+
Items []Bundle `json:"items"`
103+
}
104+
105+
func init() {
106+
SchemeBuilder.Register(&Bundle{}, &BundleList{})
107+
}
108+
109+
// GetLabels returns a base set of labels relating to the ClowdApp.
110+
func (i *Bundle) GetLabels() map[string]string {
111+
if i.Labels == nil {
112+
i.Labels = map[string]string{}
113+
}
114+
115+
if _, ok := i.Labels["Bundle"]; !ok {
116+
i.Labels["Bundle"] = i.ObjectMeta.Name
117+
}
118+
119+
newMap := make(map[string]string, len(i.Labels))
120+
121+
for k, v := range i.Labels {
122+
newMap[k] = v
123+
}
124+
125+
return newMap
126+
}

‎api/v1alpha1/frontend_types.go

+7-6
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,13 @@ type FrontendSpec struct {
3939
// Important: Run "make" to regenerate code after modifying this file
4040

4141
// Foo is an example field of Frontend. Edit frontend_types.go to remove/update
42-
EnvName string `json:"envName"`
43-
Title string `json:"title"`
44-
DeploymentRepo string `json:"deploymentRepo"`
45-
API ApiInfo `json:"API"`
46-
Frontend FrontendInfo `json:"frontend"`
47-
Image string `json:"image"`
42+
EnvName string `json:"envName"`
43+
Title string `json:"title"`
44+
DeploymentRepo string `json:"deploymentRepo"`
45+
API ApiInfo `json:"API"`
46+
Frontend FrontendInfo `json:"frontend"`
47+
Image string `json:"image"`
48+
NavItem *BundleNavItem `json:"navItem,omitempty"`
4849
}
4950

5051
// FrontendStatus defines the observed state of Frontend

‎api/v1alpha1/zz_generated.deepcopy.go

+247
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎bundle.yaml

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
apiVersion: cloud.redhat.com/v1alpha1
2+
kind: Bundle
3+
metadata:
4+
name: rhel
5+
spec:
6+
id: rhel
7+
title: RHEL
8+
appList:
9+
- inventory
10+
envName: fon
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
2+
---
3+
apiVersion: apiextensions.k8s.io/v1
4+
kind: CustomResourceDefinition
5+
metadata:
6+
annotations:
7+
controller-gen.kubebuilder.io/version: v0.4.1
8+
creationTimestamp: null
9+
name: bundles.cloud.redhat.com
10+
spec:
11+
group: cloud.redhat.com
12+
names:
13+
kind: Bundle
14+
listKind: BundleList
15+
plural: bundles
16+
singular: bundle
17+
scope: Namespaced
18+
versions:
19+
- name: v1alpha1
20+
schema:
21+
openAPIV3Schema:
22+
description: Bundle is the Schema for the Bundles API
23+
properties:
24+
apiVersion:
25+
description: 'APIVersion defines the versioned schema of this representation
26+
of an object. Servers should convert recognized schemas to the latest
27+
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
28+
type: string
29+
kind:
30+
description: 'Kind is a string value representing the REST resource this
31+
object represents. Servers may infer this from the endpoint the client
32+
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
33+
type: string
34+
metadata:
35+
type: object
36+
spec:
37+
description: BundleSpec defines the desired state of Bundle
38+
properties:
39+
appList:
40+
items:
41+
type: string
42+
type: array
43+
envName:
44+
type: string
45+
extraNavItems:
46+
items:
47+
properties:
48+
name:
49+
type: string
50+
navItem:
51+
properties:
52+
appId:
53+
type: string
54+
filterable:
55+
type: boolean
56+
groupId:
57+
type: string
58+
href:
59+
type: string
60+
isExternal:
61+
type: boolean
62+
navItems:
63+
items:
64+
properties:
65+
appId:
66+
type: string
67+
filterable:
68+
type: boolean
69+
groupId:
70+
type: string
71+
href:
72+
type: string
73+
isExternal:
74+
type: boolean
75+
permissions:
76+
items:
77+
properties:
78+
args:
79+
items:
80+
items:
81+
type: string
82+
type: array
83+
type: array
84+
method:
85+
type: string
86+
required:
87+
- args
88+
- method
89+
type: object
90+
type: array
91+
product:
92+
type: string
93+
title:
94+
type: string
95+
required:
96+
- title
97+
type: object
98+
type: array
99+
permissions:
100+
items:
101+
properties:
102+
args:
103+
items:
104+
items:
105+
type: string
106+
type: array
107+
type: array
108+
method:
109+
type: string
110+
required:
111+
- args
112+
- method
113+
type: object
114+
type: array
115+
product:
116+
type: string
117+
routes:
118+
items:
119+
properties:
120+
appId:
121+
type: string
122+
filterable:
123+
type: boolean
124+
groupId:
125+
type: string
126+
href:
127+
type: string
128+
isExternal:
129+
type: boolean
130+
permissions:
131+
items:
132+
properties:
133+
args:
134+
items:
135+
items:
136+
type: string
137+
type: array
138+
type: array
139+
method:
140+
type: string
141+
required:
142+
- args
143+
- method
144+
type: object
145+
type: array
146+
product:
147+
type: string
148+
title:
149+
type: string
150+
required:
151+
- title
152+
type: object
153+
type: array
154+
title:
155+
type: string
156+
required:
157+
- title
158+
type: object
159+
required:
160+
- name
161+
- navItem
162+
type: object
163+
type: array
164+
id:
165+
description: Foo is an example field of Bundle. Edit Bundle_types.go
166+
to remove/update
167+
type: string
168+
title:
169+
type: string
170+
required:
171+
- appList
172+
- envName
173+
- id
174+
- title
175+
type: object
176+
status:
177+
description: BundleStatus defines the observed state of Bundle
178+
type: object
179+
type: object
180+
served: true
181+
storage: true
182+
subresources:
183+
status: {}
184+
status:
185+
acceptedNames:
186+
kind: ""
187+
plural: ""
188+
conditions: []
189+
storedVersions: []

‎config/crd/bases/cloud.redhat.com_frontends.yaml

+109
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,115 @@ spec:
6262
type: object
6363
image:
6464
type: string
65+
navItem:
66+
properties:
67+
appId:
68+
type: string
69+
filterable:
70+
type: boolean
71+
groupId:
72+
type: string
73+
href:
74+
type: string
75+
isExternal:
76+
type: boolean
77+
navItems:
78+
items:
79+
properties:
80+
appId:
81+
type: string
82+
filterable:
83+
type: boolean
84+
groupId:
85+
type: string
86+
href:
87+
type: string
88+
isExternal:
89+
type: boolean
90+
permissions:
91+
items:
92+
properties:
93+
args:
94+
items:
95+
items:
96+
type: string
97+
type: array
98+
type: array
99+
method:
100+
type: string
101+
required:
102+
- args
103+
- method
104+
type: object
105+
type: array
106+
product:
107+
type: string
108+
title:
109+
type: string
110+
required:
111+
- title
112+
type: object
113+
type: array
114+
permissions:
115+
items:
116+
properties:
117+
args:
118+
items:
119+
items:
120+
type: string
121+
type: array
122+
type: array
123+
method:
124+
type: string
125+
required:
126+
- args
127+
- method
128+
type: object
129+
type: array
130+
product:
131+
type: string
132+
routes:
133+
items:
134+
properties:
135+
appId:
136+
type: string
137+
filterable:
138+
type: boolean
139+
groupId:
140+
type: string
141+
href:
142+
type: string
143+
isExternal:
144+
type: boolean
145+
permissions:
146+
items:
147+
properties:
148+
args:
149+
items:
150+
items:
151+
type: string
152+
type: array
153+
type: array
154+
method:
155+
type: string
156+
required:
157+
- args
158+
- method
159+
type: object
160+
type: array
161+
product:
162+
type: string
163+
title:
164+
type: string
165+
required:
166+
- title
167+
type: object
168+
type: array
169+
title:
170+
type: string
171+
required:
172+
- title
173+
type: object
65174
title:
66175
type: string
67176
required:

‎config/crd/kustomization.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# It should be run by config/default
44
resources:
55
- bases/cloud.redhat.com_frontends.yaml
6+
- bases/cloud.redhat.com_bundles.yaml
67
#+kubebuilder:scaffold:crdkustomizeresource
78

89
patchesStrategicMerge:

‎config/samples/_v1alpha1_bundle.yaml

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
apiVersion: cloud.redhat.com/v1alpha1
2+
kind: Bundle
3+
metadata:
4+
name: test
5+
spec:
6+
# Add fields here

‎config/samples/kustomization.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
## Append samples you want in your CSV to this file as resources ##
22
resources:
33
- _v1alpha1_frontend.yaml
4+
- _v1alpha1_bundle.yaml
45
#+kubebuilder:scaffold:manifestskustomizesamples

‎controllers/frontend_controller.go

+64-2
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,15 @@ import (
2525
networking "k8s.io/api/networking/v1"
2626
k8serr "k8s.io/apimachinery/pkg/api/errors"
2727
"k8s.io/apimachinery/pkg/runtime"
28+
"k8s.io/apimachinery/pkg/types"
2829
"k8s.io/client-go/tools/record"
2930
ctrl "sigs.k8s.io/controller-runtime"
3031
"sigs.k8s.io/controller-runtime/pkg/client"
3132
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
33+
"sigs.k8s.io/controller-runtime/pkg/handler"
3234
"sigs.k8s.io/controller-runtime/pkg/log"
35+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
36+
"sigs.k8s.io/controller-runtime/pkg/source"
3337

3438
crd "github.com/RedHatInsights/frontend-operator/api/v1alpha1"
3539
resCache "github.com/RedHatInsights/rhc-osdk-utils/resource_cache"
@@ -56,7 +60,7 @@ var cacheConfig = resCache.NewCacheConfig(scheme, FEKey("log"))
5660

5761
var CoreDeployment = cacheConfig.NewSingleResourceIdent("main", "deployment", &apps.Deployment{})
5862
var CoreService = cacheConfig.NewSingleResourceIdent("main", "service", &v1.Service{})
59-
var ConfigDeployment = cacheConfig.NewSingleResourceIdent("config", "deployment", &apps.Deployment{})
63+
var CoreConfig = cacheConfig.NewSingleResourceIdent("main", "config", &v1.ConfigMap{})
6064
var WebIngress = cacheConfig.NewMultiResourceIdent("ingress", "web_ingress", &networking.Ingress{})
6165

6266
// FrontendReconciler reconciles a Frontend object
@@ -126,7 +130,7 @@ func (r *FrontendReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
126130

127131
cache := resCache.NewObjectCache(ctx, r.Client, cacheConfig)
128132

129-
err = runReconciliation(&frontend, &cache)
133+
err = runReconciliation(ctx, r.Client, &frontend, &cache)
130134

131135
if err != nil {
132136
// SetClowdAppConditions(ctx, r.Client, &frontend, crd.ReconciliationFailed, err)
@@ -165,11 +169,69 @@ func (r *FrontendReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
165169

166170
// SetupWithManager sets up the controller with the Manager.
167171
func (r *FrontendReconciler) SetupWithManager(mgr ctrl.Manager) error {
172+
173+
cache := mgr.GetCache()
174+
175+
cache.IndexField(
176+
context.TODO(), &crd.Frontend{}, "spec.envName", func(o client.Object) []string {
177+
return []string{o.(*crd.Frontend).Spec.EnvName}
178+
})
179+
180+
cache.IndexField(
181+
context.TODO(), &crd.Bundle{}, "spec.envName", func(o client.Object) []string {
182+
return []string{o.(*crd.Bundle).Spec.EnvName}
183+
})
184+
168185
return ctrl.NewControllerManagedBy(mgr).
169186
For(&crd.Frontend{}).
187+
Watches(
188+
&source.Kind{Type: &crd.Bundle{}},
189+
handler.EnqueueRequestsFromMapFunc(r.appsToEnqueueUponBundleUpdate),
190+
).
170191
Complete(r)
171192
}
172193

194+
func (r *FrontendReconciler) appsToEnqueueUponBundleUpdate(a client.Object) []reconcile.Request {
195+
reqs := []reconcile.Request{}
196+
ctx := context.Background()
197+
obj := types.NamespacedName{
198+
Name: a.GetName(),
199+
Namespace: a.GetNamespace(),
200+
}
201+
202+
// Get the Bundle resource
203+
204+
bundle := crd.Bundle{}
205+
err := r.Client.Get(ctx, obj, &bundle)
206+
207+
if err != nil {
208+
if k8serr.IsNotFound(err) {
209+
// Must have been deleted
210+
return reqs
211+
}
212+
r.Log.Error(err, "Failed to fetch Bundle")
213+
return nil
214+
}
215+
216+
// Get all the ClowdApp resources
217+
218+
frontendList := crd.FrontendList{}
219+
r.Client.List(ctx, &frontendList, client.MatchingFields{"spec.envName": bundle.Spec.EnvName})
220+
221+
// Filter based on base attribute
222+
223+
for _, frontend := range frontendList.Items {
224+
reqs = append(reqs, reconcile.Request{
225+
NamespacedName: types.NamespacedName{
226+
Name: frontend.Name,
227+
Namespace: frontend.Namespace,
228+
},
229+
})
230+
}
231+
232+
return reqs
233+
}
234+
173235
func (r *FrontendReconciler) finalizeApp(reqLogger logr.Logger, a *crd.Frontend) error {
174236

175237
delete(managedFrontends, a.GetIdent())

‎controllers/reconcile.go

+99-2
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,29 @@
11
package controllers
22

33
import (
4+
"context"
5+
"encoding/json"
46
"fmt"
57

68
crd "github.com/RedHatInsights/frontend-operator/api/v1alpha1"
79
resCache "github.com/RedHatInsights/rhc-osdk-utils/resource_cache"
810
"github.com/RedHatInsights/rhc-osdk-utils/utils"
11+
912
apps "k8s.io/api/apps/v1"
1013
v1 "k8s.io/api/core/v1"
1114
networking "k8s.io/api/networking/v1"
1215
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1316
"k8s.io/apimachinery/pkg/types"
1417
"k8s.io/apimachinery/pkg/util/intstr"
18+
19+
"sigs.k8s.io/controller-runtime/pkg/client"
1520
)
1621

17-
func runReconciliation(frontend *crd.Frontend, cache *resCache.ObjectCache) error {
22+
func runReconciliation(context context.Context, pClient client.Client, frontend *crd.Frontend, cache *resCache.ObjectCache) error {
23+
if err := createConfigConfigMap(context, pClient, frontend, cache); err != nil {
24+
return err
25+
}
26+
1827
if err := createFrontendDeployment(frontend, cache); err != nil {
1928
return err
2029
}
@@ -60,8 +69,24 @@ func createFrontendDeployment(frontend *crd.Frontend, cache *resCache.ObjectCach
6069
ContainerPort: 80,
6170
Protocol: "TCP",
6271
}},
72+
VolumeMounts: []v1.VolumeMount{{
73+
Name: "config",
74+
MountPath: "/config/",
75+
}},
6376
}}
6477

78+
d.Spec.Template.Spec.Volumes = []v1.Volume{}
79+
d.Spec.Template.Spec.Volumes = append(d.Spec.Template.Spec.Volumes, v1.Volume{
80+
Name: "config",
81+
VolumeSource: v1.VolumeSource{
82+
ConfigMap: &v1.ConfigMapVolumeSource{
83+
LocalObjectReference: v1.LocalObjectReference{
84+
Name: frontend.Spec.EnvName,
85+
},
86+
},
87+
},
88+
})
89+
6590
d.Spec.Template.ObjectMeta.Labels = labels
6691

6792
d.Spec.Selector = &metav1.LabelSelector{MatchLabels: labels}
@@ -182,7 +207,79 @@ func createFrontendIngress(frontend *crd.Frontend, cache *resCache.ObjectCache)
182207
return nil
183208
}
184209

185-
func createConfigConfigMap() {
210+
func createConfigConfigMap(ctx context.Context, pClient client.Client, frontend *crd.Frontend, cache *resCache.ObjectCache) error {
186211
// Will need to interact directly with the client here, and not the cache because
187212
// we need to read ALL the Frontend CRDs in the Env that we care about
213+
214+
frontendList := &crd.FrontendList{}
215+
216+
if err := pClient.List(ctx, frontendList, client.MatchingFields{"spec.envName": frontend.Spec.EnvName}); err != nil {
217+
return err
218+
}
219+
220+
cacheMap := make(map[string]crd.Frontend)
221+
for _, frontend := range frontendList.Items {
222+
if frontend.Spec.NavItem == nil {
223+
continue
224+
}
225+
cacheMap[frontend.Name] = frontend
226+
}
227+
228+
bundleList := &crd.BundleList{}
229+
230+
if err := pClient.List(ctx, bundleList, client.MatchingFields{"spec.envName": frontend.Spec.EnvName}); err != nil {
231+
return err
232+
}
233+
234+
cfgMap := &v1.ConfigMap{}
235+
236+
nn := types.NamespacedName{
237+
Name: frontend.Spec.EnvName,
238+
Namespace: frontend.Namespace,
239+
}
240+
241+
if err := cache.Create(CoreConfig, nn, cfgMap); err != nil {
242+
return err
243+
}
244+
245+
labels := frontend.GetLabels()
246+
labler := utils.GetCustomLabeler(labels, nn, frontend)
247+
labler(cfgMap)
248+
249+
cfgMap.Data = map[string]string{}
250+
251+
for _, bundle := range bundleList.Items {
252+
newBundleObject := crd.ComputedBundle{
253+
ID: bundle.Spec.ID,
254+
Title: bundle.Spec.Title,
255+
NavItems: []crd.BundleNavItem{},
256+
}
257+
258+
bundleCacheMap := make(map[string]crd.BundleNavItem)
259+
for _, extraItem := range bundle.Spec.ExtraNavItems {
260+
bundleCacheMap[extraItem.Name] = extraItem.NavItem
261+
}
262+
263+
for _, app := range bundle.Spec.AppList {
264+
if retrievedFrontend, ok := cacheMap[app]; ok {
265+
newBundleObject.NavItems = append(newBundleObject.NavItems, *retrievedFrontend.Spec.NavItem)
266+
}
267+
if bundleNavItem, ok := bundleCacheMap[app]; ok {
268+
newBundleObject.NavItems = append(newBundleObject.NavItems, bundleNavItem)
269+
}
270+
}
271+
272+
jsonData, err := json.Marshal(newBundleObject)
273+
if err != nil {
274+
return err
275+
}
276+
277+
cfgMap.Data[fmt.Sprintf("%s.json", bundle.Name)] = string(jsonData)
278+
}
279+
280+
if err := cache.Update(CoreConfig, cfgMap); err != nil {
281+
return err
282+
}
283+
284+
return nil
188285
}

‎inventory.yml

+5
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,8 @@ spec:
1414
paths:
1515
- /insights/inventory
1616
image: quay.io/redhat-cloud-services/inventory-frontend
17+
navItem:
18+
appId: "inventory"
19+
title: "Inventory"
20+
href: "/insights/inventory"
21+
product: "Red Hat Insights"

0 commit comments

Comments
 (0)
Please sign in to comment.