From 455228041271c65e116fcd1293862adae815d028 Mon Sep 17 00:00:00 2001 From: Jason Hsu Date: Sat, 26 Aug 2023 23:59:54 -0700 Subject: [PATCH] helpers for CRD --- db.go | 16 ++-- instancer.go | 37 +++++++--- k8sclient.go | 74 +++++++++++++++++++ ...rd.yaml => 0-instanced-challenge-crd.yaml} | 0 operator-experiment/example-instchall.yaml | 1 + webserver.go | 2 +- 6 files changed, 110 insertions(+), 20 deletions(-) rename operator-experiment/{instanced-challenge-crd.yaml => 0-instanced-challenge-crd.yaml} (100%) diff --git a/db.go b/db.go index 50fe7a7..ef5a474 100644 --- a/db.go +++ b/db.go @@ -9,9 +9,9 @@ import ( ) type InstanceRecord struct { - id int64 - expiry time.Time - challenge string + Id int64 `json:"id"` + Expiry time.Time `json:"expiry"` + Challenge string `json:"challenge"` } func (in *Instancer) InitDB(file string) error { @@ -60,9 +60,9 @@ func (in *Instancer) InsertInstanceRecord(ttl time.Duration, challenge string) ( } return InstanceRecord{ - id: id, - expiry: expiry, - challenge: challenge, + Id: id, + Expiry: expiry, + Challenge: challenge, }, nil } @@ -97,11 +97,11 @@ func (in *Instancer) ReadInstanceRecords() ([]InstanceRecord, error) { for rows.Next() { record := InstanceRecord{} var t int64 - err = rows.Scan(&record.id, &record.challenge, &t) + err = rows.Scan(&record.Id, &record.Challenge, &t) if err != nil { return records, err } - record.expiry = time.Unix(t, 0) + record.Expiry = time.Unix(t, 0) records = append(records, record) } err = rows.Err() diff --git a/instancer.go b/instancer.go index a45c970..193fd39 100644 --- a/instancer.go +++ b/instancer.go @@ -28,6 +28,7 @@ func InitInstancer() (*Instancer, error) { echo: echo.New(), } in.echo.HideBanner = true + in.echo.HidePort = true in.log = zlog.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Level(zerolog.DebugLevel) in.echo.Logger = NewEchoLog(in.log) log := in.log.With().Str("component", "instanced-init").Logger() @@ -58,6 +59,20 @@ func InitInstancer() (*Instancer, error) { rest.SetKubernetesDefaults(in.k8sConfig) log.Debug().Str("config", fmt.Sprintf("%+v", in.k8sConfig)).Msg("loaded kube-api client config") + // Test CRDs + log.Debug().Msg("querying CRDs") + crdChallObjs, err := in.QueryInstancedChallenges("challenges") + if err != nil { + log.Debug().Err(err).Msg("error retrieving challenge definitions from CRDs") + } else { + for k, o := range crdChallObjs { + log.Debug().Str("challenge", k).Msg("parsed challenge from CRD") + for _, v := range o { + log.Debug().Str("kind", v.GetKind()).Str("name", v.GetName()).Str("challenge", k).Msg("parsed resource") + } + } + } + err = in.InitDB("/data/instancer.db") if err != nil { log.Fatal().Err(err).Msg("could not init sqlite db") @@ -77,9 +92,9 @@ func (in *Instancer) DestoryExpiredInstances() { log.Info().Int("count", len(instances)).Msg("instances found") for _, i := range instances { // Any does not marshall properly - log.Debug().Int64("id", i.id).Time("expiry", i.expiry).Str("challenge", i.challenge).Msg("instance record found") - if time.Now().After(i.expiry) { - log.Info().Int64("id", i.id).Str("challenge", i.challenge).Msg("destroying expired instance") + log.Debug().Int64("id", i.Id).Time("expiry", i.Expiry).Str("challenge", i.Challenge).Msg("instance record found") + if time.Now().After(i.Expiry) { + log.Info().Int64("id", i.Id).Str("challenge", i.Challenge).Msg("destroying expired instance") err := in.DestroyInstance(i) if err != nil { log.Error().Err(err).Msg("error destroying instance") @@ -90,20 +105,20 @@ func (in *Instancer) DestoryExpiredInstances() { func (in *Instancer) DestroyInstance(rec InstanceRecord) error { log := in.log.With().Str("component", "instanced").Logger() - chal, ok := in.challengeObjs[rec.challenge] + chal, ok := in.challengeObjs[rec.Challenge] if !ok { - return &ChallengeNotFoundError{rec.challenge} + return &ChallengeNotFoundError{rec.Challenge} } for _, o := range chal { obj := o.DeepCopy() // todo: set proper name - obj.SetName(fmt.Sprintf("in-%v-%v", obj.GetName(), rec.id)) + obj.SetName(fmt.Sprintf("in-%v-%v", obj.GetName(), rec.Id)) err := in.DeleteObject(obj, "challenges") if err != nil { log.Warn().Err(err).Str("name", obj.GetName()).Str("kind", obj.GetKind()).Msg("error deleting object") } } - err := in.DeleteInstanceRecord(rec.id) + err := in.DeleteInstanceRecord(rec.Id) if err != nil { log.Warn().Err(err).Msg("error deleting instance record") } @@ -130,16 +145,16 @@ func (in *Instancer) CreateInstance(challenge string) (InstanceRecord, error) { if err != nil { log.Error().Err(err).Msg("could not create instance record") } else { - log.Info().Time("expiry", rec.expiry). - Str("challenge", rec.challenge). - Int64("id", rec.id). + log.Info().Time("expiry", rec.Expiry). + Str("challenge", rec.Challenge). + Int64("id", rec.Id). Msg("registered new instance") } log.Info().Int("count", len(chal)).Msg("creating objects") for _, o := range chal { obj := o.DeepCopy() - obj.SetName(fmt.Sprintf("in-%v-%v", obj.GetName(), rec.id)) + obj.SetName(fmt.Sprintf("in-%v-%v", obj.GetName(), rec.Id)) resObj, err := in.CreateObject(obj, "challenges") log.Debug().Any("object", resObj).Msg("created object") if err != nil { diff --git a/k8sclient.go b/k8sclient.go index 4972ac0..434c81f 100644 --- a/k8sclient.go +++ b/k8sclient.go @@ -149,3 +149,77 @@ func (in *Instancer) GetObjectResource(unstructObj *unstructured.Unstructured) ( return mapping.Resource, nil } + +func (in *Instancer) QueryInstancedChallenges(namespace string) (map[string][]unstructured.Unstructured, error) { + resource := schema.GroupVersionResource{ + Group: "k8s.maplebacon.org", + Version: "unstable", + Resource: "instancedchallenges", + } + + client, err := dynamic.NewForConfig(in.k8sConfig) + if err != nil { + return nil, err + } + + chalList, err := client.Resource(resource).Namespace(namespace).List(context.TODO(), metav1.ListOptions{}) + if err != nil { + return nil, err + } + + ret := make(map[string][]unstructured.Unstructured) + + for _, c := range chalList.Items { + resources, found, err := unstructured.NestedSlice(c.Object, "spec", "resources") + if err != nil || !found { + fmt.Printf("resources not found for challenge crd %v: error=%v", c.GetName(), err) + continue + } + + res := make([]unstructured.Unstructured, 0) + + for _, r := range resources { + obj, ok := r.(map[string]interface{}) + if !ok { + fmt.Printf("could not parse object") + } + res = append(res, unstructured.Unstructured{Object: obj}) + } + ret[c.GetName()] = res + } + return ret, nil +} + +func (in *Instancer) QueryInstancedChallenge(name string, namespace string) ([]unstructured.Unstructured, error) { + resource := schema.GroupVersionResource{ + Group: "k8s.maplebacon.org", + Version: "unstable", + Resource: "instancedchallenges", + } + + client, err := dynamic.NewForConfig(in.k8sConfig) + if err != nil { + return nil, err + } + + chal, err := client.Resource(resource).Namespace(namespace).Get(context.TODO(), name, metav1.GetOptions{}) + if err != nil { + return nil, err + } + + resources, found, err := unstructured.NestedSlice(chal.Object, "spec", "resources") + if err != nil || !found { + fmt.Printf("resources not found for challenge crd %v: error=%v", chal.GetName(), err) + return nil, err + } + res := make([]unstructured.Unstructured, 0) + for _, r := range resources { + obj, ok := r.(map[string]interface{}) + if !ok { + fmt.Printf("could not parse object") + } + res = append(res, unstructured.Unstructured{Object: obj}) + } + + return res, nil +} diff --git a/operator-experiment/instanced-challenge-crd.yaml b/operator-experiment/0-instanced-challenge-crd.yaml similarity index 100% rename from operator-experiment/instanced-challenge-crd.yaml rename to operator-experiment/0-instanced-challenge-crd.yaml diff --git a/operator-experiment/example-instchall.yaml b/operator-experiment/example-instchall.yaml index 931cce7..36be50c 100644 --- a/operator-experiment/example-instchall.yaml +++ b/operator-experiment/example-instchall.yaml @@ -2,6 +2,7 @@ apiVersion: "k8s.maplebacon.org/unstable" kind: InstancedChallenge metadata: name: nginx + namespace: challenges spec: resources: - apiVersion: apps/v1 diff --git a/webserver.go b/webserver.go index df531cc..7e516eb 100644 --- a/webserver.go +++ b/webserver.go @@ -49,7 +49,7 @@ func (in *Instancer) handleInstanceCreate(c echo.Context) error { return c.JSON(http.StatusInternalServerError, "challenge deploy failed: contact admin") } c.Logger().Info("processed request to provision new instance") - return c.JSON(http.StatusAccepted, InstancesResponse{"created", chalName, rec.id}) + return c.JSON(http.StatusAccepted, InstancesResponse{"created", chalName, rec.Id}) } func (in *Instancer) handleInstanceDelete(c echo.Context) error {