Skip to content

Commit 654ea69

Browse files
committed
Setup oadp-cli binary server on operator startup - First pass, route and ConsoleCLIDownload will be moved into a controller
Signed-off-by: Joseph <jvaikath@redhat.com>
1 parent 3801ae3 commit 654ea69

File tree

10 files changed

+272
-10
lines changed

10 files changed

+272
-10
lines changed

bundle/manifests/oadp-operator.clusterserviceversion.yaml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -896,6 +896,18 @@ spec:
896896
- get
897897
- list
898898
- watch
899+
- apiGroups:
900+
- console.openshift.io
901+
resources:
902+
- consoleclidownloads
903+
verbs:
904+
- create
905+
- delete
906+
- get
907+
- list
908+
- patch
909+
- update
910+
- watch
899911
- apiGroups:
900912
- coordination.k8s.io
901913
- corev1
@@ -1190,6 +1202,40 @@ spec:
11901202
path: token
11911203
- emptyDir: {}
11921204
name: tmp-dir
1205+
- label:
1206+
app: oadp-cli
1207+
name: openshift-adp-oadp-cli-server
1208+
spec:
1209+
replicas: 1
1210+
selector:
1211+
matchLabels:
1212+
app: oadp-cli
1213+
strategy: {}
1214+
template:
1215+
metadata:
1216+
labels:
1217+
app: oadp-cli
1218+
spec:
1219+
containers:
1220+
- image: quay.io/rh_ee_jvaikath/oadp-cli-binaries
1221+
name: oadp-cli-server
1222+
resources:
1223+
limits:
1224+
cpu: 100m
1225+
memory: 64Mi
1226+
requests:
1227+
cpu: 50m
1228+
memory: 32Mi
1229+
securityContext:
1230+
allowPrivilegeEscalation: false
1231+
capabilities:
1232+
drop:
1233+
- ALL
1234+
readOnlyRootFilesystem: true
1235+
securityContext:
1236+
runAsNonRoot: true
1237+
serviceAccountName: openshift-adp-controller-manager
1238+
terminationGracePeriodSeconds: 10
11931239
permissions:
11941240
- rules:
11951241
- apiGroups:

bundle/manifests/oadp-operator.oadp-cli.yaml

Lines changed: 0 additions & 10 deletions
This file was deleted.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
apiVersion: v1
2+
kind: Service
3+
metadata:
4+
creationTimestamp: null
5+
name: openshift-adp-cli-server
6+
spec:
7+
ports:
8+
- name: http
9+
port: 80
10+
protocol: TCP
11+
targetPort: 8080
12+
selector:
13+
app: oadp-cli
14+
type: ClusterIP
15+
status:
16+
loadBalancer: {}

cmd/main.go

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,28 @@ import (
2323
"flag"
2424
"fmt"
2525
"os"
26+
"time"
2627

28+
"github.com/go-logr/logr"
2729
snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1"
2830
configv1 "github.com/openshift/api/config/v1"
31+
consolev1 "github.com/openshift/api/console/v1"
2932
routev1 "github.com/openshift/api/route/v1"
3033
security "github.com/openshift/api/security/v1"
3134
monitor "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
3235
velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
3336
appsv1 "k8s.io/api/apps/v1"
3437
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
38+
"k8s.io/apimachinery/pkg/api/errors"
3539
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3640
"k8s.io/apimachinery/pkg/runtime"
3741
"k8s.io/apimachinery/pkg/types"
42+
"k8s.io/apimachinery/pkg/util/intstr"
3843
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
3944
"k8s.io/client-go/discovery"
4045
"k8s.io/client-go/kubernetes"
4146
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
47+
4248
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
4349
// to ensure that exec-entrypoint and run can make use of them.
4450
_ "k8s.io/client-go/plugin/pkg/client/auth"
@@ -54,6 +60,7 @@ import (
5460
oadpv1alpha1 "github.com/openshift/oadp-operator/api/v1alpha1"
5561
"github.com/openshift/oadp-operator/internal/controller"
5662
pkgclient "github.com/openshift/oadp-operator/pkg/client"
63+
5764
//+kubebuilder:scaffold:imports
5865
"github.com/openshift/oadp-operator/pkg/credentials/stsflow"
5966
"github.com/openshift/oadp-operator/pkg/leaderelection"
@@ -86,6 +93,32 @@ func init() {
8693
//+kubebuilder:scaffold:scheme
8794
}
8895

96+
// oadpCLISetupRunnable implements manager.Runnable to set up OADP CLI downloads after cache starts
97+
type oadpCLISetupRunnable struct {
98+
client client.Client
99+
namespace string
100+
log logr.Logger
101+
}
102+
103+
func (r *oadpCLISetupRunnable) Start(ctx context.Context) error {
104+
// Run setup in a goroutine and keep the runnable alive
105+
go func() {
106+
r.log.Info("setting up OADP CLI download resources")
107+
if err := setupOADPCLIDownload(ctx, r.client, r.namespace); err != nil {
108+
r.log.Error(err, "unable to setup OADP CLI download, continuing anyway")
109+
}
110+
}()
111+
112+
// Block until context is cancelled (manager shutdown)
113+
<-ctx.Done()
114+
return nil
115+
}
116+
117+
// NeedLeaderElection returns false so this runs even if not the leader
118+
func (r *oadpCLISetupRunnable) NeedLeaderElection() bool {
119+
return true
120+
}
121+
89122
func main() {
90123
var metricsAddr string
91124
var enableLeaderElection bool
@@ -200,6 +233,11 @@ func main() {
200233
os.Exit(1)
201234
}
202235

236+
if err := consolev1.AddToScheme(mgr.GetScheme()); err != nil {
237+
setupLog.Error(err, "unable to add OpenShift console API to scheme")
238+
os.Exit(1)
239+
}
240+
203241
if err := velerov1.AddToScheme(mgr.GetScheme()); err != nil {
204242
setupLog.Error(err, "unable to add Velero APIs to scheme")
205243
os.Exit(1)
@@ -275,6 +313,16 @@ func main() {
275313
os.Exit(1)
276314
}
277315

316+
// Add OADP CLI download setup as a manager runnable
317+
if err := mgr.Add(&oadpCLISetupRunnable{
318+
client: mgr.GetClient(),
319+
namespace: watchNamespace,
320+
log: setupLog,
321+
}); err != nil {
322+
setupLog.Error(err, "unable to add OADP CLI setup runnable")
323+
os.Exit(1)
324+
}
325+
278326
setupLog.Info("starting manager")
279327
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
280328
setupLog.Error(err, "problem running manager")
@@ -348,3 +396,95 @@ func DoesCRDExist(CRDGroupVersion, CRDName string, kubeconf *rest.Config) (bool,
348396
}
349397
return discoveryResult, nil
350398
}
399+
400+
func setupOADPCLIDownload(ctx context.Context, c client.Client, namespace string) error {
401+
// Create Route, wait for hostname, create ConsoleCLIDownload
402+
// Implementation goes here
403+
route := &routev1.Route{
404+
ObjectMeta: metav1.ObjectMeta{
405+
Name: "openshift-adp-cli-server-route",
406+
Namespace: namespace,
407+
},
408+
Spec: routev1.RouteSpec{
409+
To: routev1.RouteTargetReference{
410+
Kind: "Service",
411+
Name: "openshift-adp-cli-server",
412+
},
413+
Port: &routev1.RoutePort{
414+
TargetPort: intstr.FromString("http"),
415+
},
416+
TLS: &routev1.TLSConfig{
417+
Termination: routev1.TLSTerminationEdge,
418+
InsecureEdgeTerminationPolicy: routev1.InsecureEdgeTerminationPolicyRedirect,
419+
},
420+
},
421+
}
422+
err := c.Create(ctx, route)
423+
if err != nil && !errors.IsAlreadyExists(err) {
424+
return err
425+
}
426+
if errors.IsAlreadyExists(err) {
427+
// Route already exists, just get it
428+
err = c.Get(ctx, client.ObjectKey{
429+
Name: "openshift-adp-cli-server-route",
430+
Namespace: namespace,
431+
}, route)
432+
if err != nil {
433+
return fmt.Errorf("failed to get existing route: %w", err)
434+
}
435+
}
436+
437+
hostname := ""
438+
// 2. Get hostname from Status
439+
for i := 0; i < 3; i++ {
440+
err = c.Get(ctx, client.ObjectKey{
441+
Name: "openshift-adp-cli-server-route",
442+
Namespace: namespace,
443+
}, route)
444+
445+
if err != nil {
446+
return fmt.Errorf("failed to get route: %w", err)
447+
}
448+
449+
// Check if hostname is assigned
450+
if len(route.Status.Ingress) > 0 && route.Status.Ingress[0].Host != "" {
451+
hostname = route.Status.Ingress[0].Host
452+
break
453+
}
454+
455+
// Backoff: wait 2^i seconds (1s, 2s, 4s)
456+
if i < 2 {
457+
setupLog.Info("route hostname not ready, retrying...", "attempt", i+1)
458+
time.Sleep(time.Duration(1<<uint(i)) * time.Second)
459+
}
460+
}
461+
462+
if hostname == "" {
463+
return fmt.Errorf("failed to get route hostname, oadp-cli-server is not ready")
464+
}
465+
466+
// 3. Create ConsoleCLIDownload with the hostname
467+
downloadURL := fmt.Sprintf("https://%s/", hostname)
468+
469+
// Create the ConsoleCLIDownload (cluster-scoped resource, no namespace)
470+
consoleCLIDownload := &consolev1.ConsoleCLIDownload{
471+
ObjectMeta: metav1.ObjectMeta{
472+
Name: "openshift-adp-oadp-cli",
473+
},
474+
Spec: consolev1.ConsoleCLIDownloadSpec{
475+
Description: "OADP operator Command Line Interface (CLI)",
476+
DisplayName: "oadp - OADP operator Command Line Interface (CLI)",
477+
Links: []consolev1.CLIDownloadLink{
478+
{
479+
Href: downloadURL,
480+
Text: "Download OADP CLI for Linux x86_64",
481+
},
482+
},
483+
},
484+
}
485+
err = c.Create(ctx, consoleCLIDownload)
486+
if err != nil && !errors.IsAlreadyExists(err) {
487+
return fmt.Errorf("failed to create ConsoleCLIDownload: %w", err)
488+
}
489+
return nil
490+
}

config/default/kustomization.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ resources:
1818
- ../crd
1919
- ../rbac
2020
- ../manager
21+
- ../oadp-cli
2122
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
2223
# crd/kustomization.yaml
2324
#- ../webhook
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: oadp-cli-server
5+
namespace: system
6+
labels:
7+
app: oadp-cli
8+
spec:
9+
replicas: 1
10+
selector:
11+
matchLabels:
12+
app: oadp-cli
13+
template:
14+
metadata:
15+
labels:
16+
app: oadp-cli
17+
spec:
18+
serviceAccountName: controller-manager
19+
securityContext:
20+
runAsNonRoot: true
21+
containers:
22+
- name: oadp-cli-server
23+
image: quay.io/rh_ee_jvaikath/oadp-cli-binaries
24+
resources:
25+
limits:
26+
cpu: 100m
27+
memory: 64Mi
28+
requests:
29+
cpu: 50m
30+
memory: 32Mi
31+
securityContext:
32+
allowPrivilegeEscalation: false
33+
capabilities:
34+
drop:
35+
- ALL
36+
readOnlyRootFilesystem: true
37+
terminationGracePeriodSeconds: 10

config/oadp-cli/kustomization.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
apiVersion: kustomize.config.k8s.io/v1beta1
2+
kind: Kustomization
3+
resources:
4+
- binary-deployment.yaml
5+
- service.yaml

config/oadp-cli/service.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
apiVersion: v1
2+
kind: Service
3+
metadata:
4+
name: cli-server
5+
namespace: system
6+
spec:
7+
selector:
8+
app: oadp-cli
9+
ports:
10+
- name: http
11+
port: 80 # Standard HTTP port
12+
targetPort: 8080 # But your container still listens on 8080
13+
protocol: TCP
14+
type: ClusterIP

config/rbac/role.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,18 @@ rules:
6464
- get
6565
- list
6666
- watch
67+
- apiGroups:
68+
- console.openshift.io
69+
resources:
70+
- consoleclidownloads
71+
verbs:
72+
- create
73+
- delete
74+
- get
75+
- list
76+
- patch
77+
- update
78+
- watch
6779
- apiGroups:
6880
- coordination.k8s.io
6981
- corev1

internal/controller/dataprotectionapplication_controller.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ var debugMode = os.Getenv("DEBUG") == "true"
7171
//+kubebuilder:rbac:groups="",resources=namespaces,verbs=get;list;watch;create;update;patch
7272
//+kubebuilder:rbac:groups=apps,resources=deployments;daemonsets,verbs=get;list;watch;create;update;patch;delete
7373
//+kubebuilder:rbac:groups=route.openshift.io,resources=routes,verbs=get;list;watch;create;update;patch;delete
74+
//+kubebuilder:rbac:groups=console.openshift.io,resources=consoleclidownloads,verbs=get;list;watch;create;update;patch;delete
7475
//+kubebuilder:rbac:groups=monitoring.coreos.com,resources=servicemonitors,verbs=get;list;watch;create;update;patch;delete
7576

7677
// Reconcile is part of the main Kubernetes reconciliation loop which aims to

0 commit comments

Comments
 (0)