@@ -17,19 +17,27 @@ limitations under the License.
1717package main
1818
1919import (
20+ "context"
2021 "flag"
22+ "fmt"
2123 "os"
24+ "strings"
2225 "time"
2326
2427 instascale "github.com/project-codeflare/instascale/controllers"
28+ instascaleconfig "github.com/project-codeflare/instascale/pkg/config"
2529 mcadv1beta1 "github.com/project-codeflare/multi-cluster-app-dispatcher/pkg/apis/controller/v1beta1"
2630 quotasubtreev1 "github.com/project-codeflare/multi-cluster-app-dispatcher/pkg/apis/quotaplugins/quotasubtree/v1"
2731 mcadconfig "github.com/project-codeflare/multi-cluster-app-dispatcher/pkg/config"
2832 mcad "github.com/project-codeflare/multi-cluster-app-dispatcher/pkg/controller/queuejob"
2933 "go.uber.org/zap/zapcore"
3034
35+ corev1 "k8s.io/api/core/v1"
36+ apierrors "k8s.io/apimachinery/pkg/api/errors"
37+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3138 "k8s.io/apimachinery/pkg/runtime"
3239 utilruntime "k8s.io/apimachinery/pkg/util/runtime"
40+ "k8s.io/client-go/kubernetes"
3341 clientgoscheme "k8s.io/client-go/kubernetes/scheme"
3442 _ "k8s.io/client-go/plugin/pkg/client/auth"
3543 "k8s.io/client-go/rest"
@@ -38,6 +46,7 @@ import (
3846 ctrl "sigs.k8s.io/controller-runtime"
3947 "sigs.k8s.io/controller-runtime/pkg/healthz"
4048 "sigs.k8s.io/controller-runtime/pkg/log/zap"
49+ "sigs.k8s.io/yaml"
4150
4251 "github.com/project-codeflare/codeflare-operator/pkg/config"
4352 // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
@@ -57,6 +66,11 @@ func init() {
5766}
5867
5968func main () {
69+ var configMapName string
70+ flag .StringVar (& configMapName , "config" , "codeflare-operator-config" ,
71+ "The name of the ConfigMap to load the operator configuration from. " +
72+ "If it does not exist, the operator will create and initialise it." )
73+
6074 zapOptions := zap.Options {
6175 Development : true ,
6276 TimeEncoder : zapcore .TimeEncoderOfLayout (time .RFC3339 ),
@@ -67,20 +81,41 @@ func main() {
6781
6882 ctx := ctrl .SetupSignalHandler ()
6983
70- cfg := config.CodeFlareOperatorConfiguration {
84+ cfg := & config.CodeFlareOperatorConfiguration {
85+ ClientConnection : & config.ClientConnection {
86+ QPS : pointer .Float32 (50 ),
87+ Burst : pointer .Int32 (100 ),
88+ },
7189 ControllerManager : config.ControllerManager {
90+ Metrics : config.MetricsConfiguration {
91+ BindAddress : ":8080" ,
92+ },
93+ Health : config.HealthConfiguration {
94+ BindAddress : ":8081" ,
95+ ReadinessEndpointName : "readyz" ,
96+ LivenessEndpointName : "healthz" ,
97+ },
7298 LeaderElection : & configv1alpha1.LeaderElectionConfiguration {},
7399 },
74- MCAD : & mcadconfig.MCADConfiguration {},
75- InstaScale : & config.InstaScaleConfiguration {},
100+ MCAD : & mcadconfig.MCADConfiguration {},
101+ InstaScale : & config.InstaScaleConfiguration {
102+ Enabled : pointer .Bool (false ),
103+ InstaScaleConfiguration : instascaleconfig.InstaScaleConfiguration {
104+ MaxScaleoutAllowed : 5 ,
105+ },
106+ },
76107 }
77108
78109 kubeConfig , err := ctrl .GetConfig ()
79110 exitOnError (err , "unable to get client config" )
80-
81111 if kubeConfig .UserAgent == "" {
82112 kubeConfig .UserAgent = "codeflare-operator"
83113 }
114+ kubeClient , err := kubernetes .NewForConfig (kubeConfig )
115+ exitOnError (err , "unable to create Kubernetes client" )
116+
117+ exitOnError (loadIntoOrCreate (ctx , kubeClient , namespaceOrDie (), configMapName , cfg ), "unable to initialise configuration" )
118+
84119 kubeConfig .Burst = int (pointer .Int32Deref (cfg .ClientConnection .Burst , int32 (rest .DefaultBurst )))
85120 kubeConfig .QPS = pointer .Float32Deref (cfg .ClientConnection .QPS , rest .DefaultQPS )
86121 setupLog .V (2 ).Info ("REST client" , "qps" , kubeConfig .QPS , "burst" , kubeConfig .Burst )
@@ -122,6 +157,66 @@ func main() {
122157 exitOnError (mgr .Start (ctx ), "error running manager" )
123158}
124159
160+ func loadIntoOrCreate (ctx context.Context , client kubernetes.Interface , ns , name string , cfg * config.CodeFlareOperatorConfiguration ) error {
161+ configMap , err := client .CoreV1 ().ConfigMaps (ns ).Get (ctx , name , metav1.GetOptions {})
162+ if apierrors .IsNotFound (err ) {
163+ return createConfigMap (ctx , client , ns , name , cfg )
164+ } else if err != nil {
165+ return err
166+ }
167+
168+ if len (configMap .Data ) != 1 {
169+ return fmt .Errorf ("cannot resolve config from ConfigMap %s/%s" , configMap .Namespace , configMap .Name )
170+ }
171+
172+ for _ , data := range configMap .Data {
173+ return yaml .Unmarshal ([]byte (data ), cfg )
174+ }
175+
176+ return nil
177+ }
178+
179+ func createConfigMap (ctx context.Context , client kubernetes.Interface , ns , name string , cfg * config.CodeFlareOperatorConfiguration ) error {
180+ content , err := yaml .Marshal (cfg )
181+ if err != nil {
182+ return err
183+ }
184+
185+ configMap := & corev1.ConfigMap {
186+ TypeMeta : metav1.TypeMeta {
187+ Kind : "ConfigMap" ,
188+ APIVersion : "v1" ,
189+ },
190+ ObjectMeta : metav1.ObjectMeta {
191+ Name : name ,
192+ Namespace : ns ,
193+ },
194+ Data : map [string ]string {
195+ "config.yaml" : string (content ),
196+ },
197+ }
198+
199+ _ , err = client .CoreV1 ().ConfigMaps (ns ).Create (ctx , configMap , metav1.CreateOptions {})
200+ return err
201+ }
202+
203+ func namespaceOrDie () string {
204+ // This way assumes you've set the NAMESPACE environment variable either manually, when running
205+ // the operator standalone, or using the downward API, when running the operator in-cluster.
206+ if ns := os .Getenv ("NAMESPACE" ); ns != "" {
207+ return ns
208+ }
209+
210+ // Fall back to the namespace associated with the service account token, if available
211+ if data , err := os .ReadFile ("/var/run/secrets/kubernetes.io/serviceaccount/namespace" ); err == nil {
212+ if ns := strings .TrimSpace (string (data )); len (ns ) > 0 {
213+ return ns
214+ }
215+ }
216+
217+ panic ("unable to determine current namespace" )
218+ }
219+
125220func exitOnError (err error , msg string ) {
126221 if err != nil {
127222 setupLog .Error (err , msg )
0 commit comments