Skip to content

Commit 8fb412e

Browse files
sgaistleafty
andauthored
feat: implement support for OpenShift wrt service account (#1013)
* feat: implement support for OpenShift wrt service account Curently both serviceAccount and serviceAccountName must be set on a Pod/Deployment/StatefulSet. More details in the documentation: https://docs.redhat.com/en/documentation/openshift_container_platform/4.17/html/building_applications/deployments#deployments-running-pod-svc-acct_deployment-operations * refactor: rewrite cluster type detection to avoid boolean flag parameters * fix(controller): update image for oauth2-proxy The original bitnami image has been phased out. * refactor: ClusterType rather than K8sVariant Co-authored-by: Flora Thiebaut <flora.thiebaut@sdsc.ethz.ch> * refactor: remove Unknown cluster type In case of detection failure, it's the error that is important not the string value. * chore: make the cluster detection information more generic --------- Co-authored-by: Flora Thiebaut <flora.thiebaut@sdsc.ethz.ch>
1 parent e0069de commit 8fb412e

7 files changed

Lines changed: 120 additions & 19 deletions

File tree

api/v1alpha1/amaltheasession_children.go

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ func (cr *AmaltheaSession) SessionVolumes() ([]v1.Volume, []v1.VolumeMount) {
8787
}
8888

8989
// StatefulSet returns a AmaltheaSession StatefulSet object
90-
func (cr *AmaltheaSession) StatefulSet() (appsv1.StatefulSet, error) {
90+
func (cr *AmaltheaSession) StatefulSet(clusterType ClusterType) (appsv1.StatefulSet, error) {
9191
labels := labelsForAmaltheaSession(cr.Name)
9292
replicas := int32(1)
9393
if cr.Spec.Hibernated {
@@ -179,6 +179,25 @@ func (cr *AmaltheaSession) StatefulSet() (appsv1.StatefulSet, error) {
179179
imagePullSecrets = append(imagePullSecrets, v1.LocalObjectReference{Name: sec.Name})
180180
}
181181

182+
pod := v1.PodSpec{
183+
ServiceAccountName: cr.Spec.ServiceAccountName,
184+
EnableServiceLinks: ptr.To(false),
185+
AutomountServiceAccountToken: ptr.To(false),
186+
SecurityContext: &v1.PodSecurityContext{FSGroup: &cr.Spec.Session.RunAsGroup},
187+
Containers: containers,
188+
InitContainers: initContainers,
189+
Volumes: volumes,
190+
Tolerations: cr.Spec.Tolerations,
191+
NodeSelector: cr.Spec.NodeSelector,
192+
Affinity: cr.Spec.Affinity,
193+
PriorityClassName: cr.Spec.PriorityClassName,
194+
ImagePullSecrets: imagePullSecrets,
195+
}
196+
197+
if clusterType == OpenShift {
198+
pod.DeprecatedServiceAccount = pod.ServiceAccountName
199+
}
200+
182201
sts := appsv1.StatefulSet{
183202
ObjectMeta: metav1.ObjectMeta{
184203
Name: cr.Name,
@@ -196,20 +215,7 @@ func (cr *AmaltheaSession) StatefulSet() (appsv1.StatefulSet, error) {
196215
ObjectMeta: metav1.ObjectMeta{
197216
Labels: labels,
198217
},
199-
Spec: v1.PodSpec{
200-
ServiceAccountName: cr.Spec.ServiceAccountName,
201-
EnableServiceLinks: ptr.To(false),
202-
AutomountServiceAccountToken: ptr.To(false),
203-
SecurityContext: &v1.PodSecurityContext{FSGroup: &cr.Spec.Session.RunAsGroup},
204-
Containers: containers,
205-
InitContainers: initContainers,
206-
Volumes: volumes,
207-
Tolerations: cr.Spec.Tolerations,
208-
NodeSelector: cr.Spec.NodeSelector,
209-
Affinity: cr.Spec.Affinity,
210-
PriorityClassName: cr.Spec.PriorityClassName,
211-
ImagePullSecrets: imagePullSecrets,
212-
},
218+
Spec: pod,
213219
},
214220
},
215221
}

api/v1alpha1/detection.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package v1alpha1
2+
3+
import (
4+
"k8s.io/client-go/discovery"
5+
"k8s.io/client-go/rest"
6+
)
7+
8+
type ClusterType string
9+
10+
const Kubernetes ClusterType = "kubernetes"
11+
const OpenShift ClusterType = "openshift"
12+
13+
func DetectClusterType(config *rest.Config) (ClusterType, error) {
14+
dcl, err := discovery.NewDiscoveryClientForConfig(config)
15+
if err != nil {
16+
return "", err
17+
}
18+
19+
apiList, err := dcl.ServerGroups()
20+
if err != nil {
21+
return "", err
22+
}
23+
24+
apiGroups := apiList.Groups
25+
for i := range apiGroups {
26+
if apiGroups[i].Name == "project.openshift.io" {
27+
return OpenShift, nil
28+
}
29+
}
30+
31+
return Kubernetes, nil
32+
}

api/v1alpha1/detection_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package v1alpha1
2+
3+
import (
4+
"encoding/json"
5+
"net/http"
6+
"net/http/httptest"
7+
"testing"
8+
9+
"github.com/stretchr/testify/assert"
10+
"github.com/stretchr/testify/require"
11+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12+
"k8s.io/client-go/rest"
13+
)
14+
15+
func TestDetectPlatformBasedOnAvailableAPIGroups(t *testing.T) {
16+
for _, tt := range []struct {
17+
apiGroupList *metav1.APIGroupList
18+
expected ClusterType
19+
}{
20+
{
21+
&metav1.APIGroupList{},
22+
Kubernetes,
23+
},
24+
{
25+
&metav1.APIGroupList{
26+
Groups: []metav1.APIGroup{
27+
{
28+
Name: "project.openshift.io",
29+
},
30+
},
31+
},
32+
OpenShift,
33+
},
34+
} {
35+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
36+
output, err := json.Marshal(tt.apiGroupList)
37+
assert.NoError(t, err)
38+
39+
w.Header().Set("Content-Type", "application/json")
40+
w.WriteHeader(http.StatusOK)
41+
_, err = w.Write(output)
42+
assert.NoError(t, err)
43+
}))
44+
defer server.Close()
45+
46+
clusterType, err := DetectClusterType(&rest.Config{Host: server.URL})
47+
require.NoError(t, err)
48+
49+
assert.Equal(t, tt.expected, clusterType)
50+
}
51+
}

cmd/amalthea/main.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,10 +170,20 @@ func main() {
170170

171171
metricsClient := metricsv.NewForConfigOrDie(config).MetricsV1beta1()
172172

173+
clusterType, err := amaltheadevv1alpha1.DetectClusterType(config)
174+
175+
if err != nil {
176+
setupLog.Error(err, "failed to do cluster detection")
177+
os.Exit(1)
178+
}
179+
180+
setupLog.Info("cluster type detected", "type", clusterType)
181+
173182
err = (&controller.AmaltheaSessionReconciler{
174183
Client: mgr.GetClient(),
175184
Scheme: mgr.GetScheme(),
176185
MetricsClient: metricsClient,
186+
ClusterType: clusterType,
177187
}).SetupWithManager(mgr)
178188

179189
if err != nil {

controller/templates/statefulset.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ spec:
152152

153153
{% if oidc["enabled"] %}
154154
- name: oauth2-proxy
155-
image: "bitnami/oauth2-proxy:7.4.0"
155+
image: "harbor.renkulab.io/bitnami-mirror/oauth2-proxy:7.6.0"
156156
securityContext:
157157
allowPrivilegeEscalation: false
158158
runAsNonRoot: true

internal/controller/amaltheasession_controller.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ type AmaltheaSessionReconciler struct {
4343
client.Client
4444
Scheme *runtime.Scheme
4545
MetricsClient metricsv1beta1.PodMetricsesGetter
46+
ClusterType amaltheadevv1alpha1.ClusterType
4647
}
4748

4849
// finalizers
@@ -145,7 +146,7 @@ func (r *AmaltheaSessionReconciler) Reconcile(ctx context.Context, req ctrl.Requ
145146

146147
log.Info("spec", "cr", amaltheasession)
147148

148-
children, err := NewChildResources(amaltheasession)
149+
children, err := NewChildResources(amaltheasession, r.ClusterType)
149150
if err != nil {
150151
log.Error(
151152
err,
@@ -157,6 +158,7 @@ func (r *AmaltheaSessionReconciler) Reconcile(ctx context.Context, req ctrl.Requ
157158
)
158159
return ctrl.Result{}, err
159160
}
161+
160162
updates, err := children.Reconcile(ctx, r.Client, amaltheasession)
161163
if err != nil {
162164
log.Error(err, "Failed when reconciling children")

internal/controller/children.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,12 +255,12 @@ func (c ChildResource[T]) Reconcile(ctx context.Context, clnt client.Client, cr
255255
}
256256
}
257257

258-
func NewChildResources(cr *amaltheadevv1alpha1.AmaltheaSession) (ChildResources, error) {
258+
func NewChildResources(cr *amaltheadevv1alpha1.AmaltheaSession, clusterType amaltheadevv1alpha1.ClusterType) (ChildResources, error) {
259259
metadata := metav1.ObjectMeta{Name: cr.Name, Namespace: cr.Namespace}
260260
secretMetadata := metav1.ObjectMeta{Name: cr.InternalSecretName(), Namespace: cr.Namespace}
261261
desiredService := cr.Service()
262262
desiredPVC := cr.PVC()
263-
desiredStatefulSet, err := cr.StatefulSet()
263+
desiredStatefulSet, err := cr.StatefulSet(clusterType)
264264
if err != nil {
265265
return ChildResources{}, err
266266
}

0 commit comments

Comments
 (0)