From 8142de521346de71788ac0728dd321d866cbde6b Mon Sep 17 00:00:00 2001 From: dviejokfs Date: Thu, 4 Aug 2022 20:27:56 +0200 Subject: [PATCH] Add update command + config parameters for operatorui and operatorapi Signed-off-by: dviejokfs --- .../templates/deployment.yaml | 6 + charts/hlf-operator-api/values.yaml | 3 + charts/hlf-operator-ui/values.yaml | 4 +- controllers/operatorui/operatorui.go | 10 +- controllers/operatorui/types.go | 2 +- kubectl-hlf/cmd/helpers/constants.go | 4 +- kubectl-hlf/cmd/operatorapi/create.go | 19 ++- kubectl-hlf/cmd/operatorapi/operatorapi.go | 1 + kubectl-hlf/cmd/operatorapi/update.go | 131 ++++++++++++++++++ kubectl-hlf/cmd/operatorui/create.go | 21 ++- kubectl-hlf/cmd/operatorui/operatorui.go | 1 + kubectl-hlf/cmd/operatorui/update.go | 123 ++++++++++++++++ .../docs/operator-ui/deploy-operator-ui.md | 5 + 13 files changed, 314 insertions(+), 16 deletions(-) create mode 100644 kubectl-hlf/cmd/operatorapi/update.go create mode 100644 kubectl-hlf/cmd/operatorui/update.go diff --git a/charts/hlf-operator-api/templates/deployment.yaml b/charts/hlf-operator-api/templates/deployment.yaml index 2157559c..181ca99a 100644 --- a/charts/hlf-operator-api/templates/deployment.yaml +++ b/charts/hlf-operator-api/templates/deployment.yaml @@ -64,6 +64,12 @@ spec: - "{{.Values.hlf.mspID}}" - "--user" - "{{.Values.hlf.user}}" + {{ end }} + {{ if and .Values.auth.oidcJWKS .Values.auth.oidcIssuer }} + - "--auth-issuer" + - "{{ .Values.auth.oidcIssuer }}" + - "--auth-jwks" + - "{{ .Values.auth.oidcJWKS }}" {{ end }} livenessProbe: httpGet: diff --git a/charts/hlf-operator-api/values.yaml b/charts/hlf-operator-api/values.yaml index 1bb647a3..c8898433 100644 --- a/charts/hlf-operator-api/values.yaml +++ b/charts/hlf-operator-api/values.yaml @@ -11,6 +11,9 @@ image: tag: "" +auth: + oidcJWKS: "" + oidcIssuer: "" hlf: mspID: "" diff --git a/charts/hlf-operator-ui/values.yaml b/charts/hlf-operator-ui/values.yaml index b474833b..547ca729 100644 --- a/charts/hlf-operator-ui/values.yaml +++ b/charts/hlf-operator-ui/values.yaml @@ -14,8 +14,8 @@ image: logoUrl: "" auth: - oidcAuthority: "", - oidcClientId: "", + oidcAuthority: "" + oidcClientId: "" oidcScope: "" imagePullSecrets: [] diff --git a/controllers/operatorui/operatorui.go b/controllers/operatorui/operatorui.go index 8c58b27c..a6a88eb3 100644 --- a/controllers/operatorui/operatorui.go +++ b/controllers/operatorui/operatorui.go @@ -64,7 +64,7 @@ type Status struct { NodePort int } -func GetConsoleState(conf *action.Configuration, config *rest.Config, releaseName string, ns string) (*hlfv1alpha1.FabricOperatorUIStatus, error) { +func GetOperatorUIState(conf *action.Configuration, config *rest.Config, releaseName string, ns string) (*hlfv1alpha1.FabricOperatorUIStatus, error) { ctx := context.Background() cmd := action.NewGet(conf) rel, err := cmd.Run(releaseName) @@ -272,7 +272,7 @@ func (r *FabricOperatorUIReconciler) Reconcile(req ctrl.Request) (ctrl.Result, e r.setConditionStatus(ctx, fabricOpConsole, hlfv1alpha1.FailedStatus, false, err, false) return r.updateCRStatusOrFailReconcile(ctx, r.Log, fabricOpConsole) } - s, err := GetConsoleState(cfg, r.Config, releaseName, ns) + s, err := GetOperatorUIState(cfg, r.Config, releaseName, ns) if err != nil { r.setConditionStatus(ctx, fabricOpConsole, hlfv1alpha1.FailedStatus, false, err, false) return r.updateCRStatusOrFailReconcile(ctx, r.Log, fabricOpConsole) @@ -480,11 +480,7 @@ func GetConfig(conf *hlfv1alpha1.FabricOperatorUI) (*HLFOperatorUIChart, error) Hosts: hosts, } } - auth := Auth{ - OIDCAuthority: spec.Auth.OIDCAuthority, - OIDCClientId: spec.Auth.OIDCClientId, - OIDCScope: spec.Auth.OIDCScope, - } + auth := Auth{} if spec.Auth != nil { auth.OIDCAuthority = spec.Auth.OIDCAuthority auth.OIDCClientId = spec.Auth.OIDCClientId diff --git a/controllers/operatorui/types.go b/controllers/operatorui/types.go index f17bd405..13a89aab 100644 --- a/controllers/operatorui/types.go +++ b/controllers/operatorui/types.go @@ -66,7 +66,7 @@ type HLFOperatorUIChart struct { Tolerations []corev1.Toleration `json:"tolerations,omitempty"` Affinity *corev1.Affinity `json:"affinity"` LogoURL string `json:"logoUrl"` - Auth Auth `json:"auth"` + Auth Auth `json:"auth,omitempty"` } type Auth struct { diff --git a/kubectl-hlf/cmd/helpers/constants.go b/kubectl-hlf/cmd/helpers/constants.go index 37b2fa88..466cb3c3 100644 --- a/kubectl-hlf/cmd/helpers/constants.go +++ b/kubectl-hlf/cmd/helpers/constants.go @@ -17,10 +17,10 @@ const ( DefaultOperationsConsoleVersion = "latest" DefaultOperationsOperatorUIImage = "ghcr.io/kfsoftware/hlf-operator-ui" - DefaultOperationsOperatorUIVersion = "0.0.7" + DefaultOperationsOperatorUIVersion = "0.0.10" DefaultOperationsOperatorAPIImage = "ghcr.io/kfsoftware/hlf-operator-api" - DefaultOperationsOperatorAPIVersion = "v0.0.7" + DefaultOperationsOperatorAPIVersion = "v0.0.10" DefaultFSServerImage = "quay.io/kfsoftware/fs-peer" DefaultFSServerVersion = "amd64-2.2.0-0.0.1" diff --git a/kubectl-hlf/cmd/operatorapi/create.go b/kubectl-hlf/cmd/operatorapi/create.go index 57f20e46..fa50b40b 100644 --- a/kubectl-hlf/cmd/operatorapi/create.go +++ b/kubectl-hlf/cmd/operatorapi/create.go @@ -26,9 +26,15 @@ type Options struct { MSPID string User string HLFKey string + OIDCIssuer string + OIDCJWKS string + Replicas int } func (o Options) Validate() error { + if o.Replicas < 1 { + return fmt.Errorf("replicas must be greater than 0") + } return nil } @@ -88,9 +94,13 @@ func (c *createCmd) run() error { Image: c.apiOpts.Image, Tag: c.apiOpts.Version, ImagePullPolicy: "Always", - Istio: v1alpha1.FabricIstio{}, - Ingress: ingress, - Replicas: 1, + Auth: &v1alpha1.FabricOperatorAPIAuth{ + OIDCJWKS: c.apiOpts.OIDCJWKS, + OIDCIssuer: c.apiOpts.OIDCIssuer, + }, + Istio: v1alpha1.FabricIstio{}, + Ingress: ingress, + Replicas: c.apiOpts.Replicas, HLFConfig: v1alpha1.FabricOperatorAPIHLFConfig{ MSPID: c.apiOpts.MSPID, User: c.apiOpts.User, @@ -153,6 +163,9 @@ func newCreateOperatorAPICmd(out io.Writer, errOut io.Writer) *cobra.Command { f.StringVarP(&c.apiOpts.User, "hlf-user", "", "", "HLF Network Config User") f.StringVarP(&c.apiOpts.HLFSecretName, "hlf-secret", "", "", "HLF Network Config Secret name") f.StringVarP(&c.apiOpts.HLFKey, "hlf-secret-key", "", "", "HLF Network Config Secret key") + f.StringVarP(&c.apiOpts.OIDCJWKS, "oidc-jwks", "", "", "OIDC JWKS URL") + f.StringVarP(&c.apiOpts.OIDCIssuer, "oidc-issuer", "", "", "OIDC Issuer URL") + f.IntVarP(&c.apiOpts.Replicas, "replicas", "", 1, "Number of replicas of the Operator UI") f.StringArrayVarP(&c.apiOpts.Hosts, "hosts", "", []string{}, "External hosts") f.BoolVarP(&c.apiOpts.Output, "output", "o", false, "Output in yaml") return cmd diff --git a/kubectl-hlf/cmd/operatorapi/operatorapi.go b/kubectl-hlf/cmd/operatorapi/operatorapi.go index 04901123..d3cd4445 100644 --- a/kubectl-hlf/cmd/operatorapi/operatorapi.go +++ b/kubectl-hlf/cmd/operatorapi/operatorapi.go @@ -12,6 +12,7 @@ func NewOperatorAPICMD(out io.Writer, errOut io.Writer) *cobra.Command { cmd.AddCommand( newCreateOperatorAPICmd(out, errOut), newDeleteOperatorAPICmd(out, errOut), + newUpdateOperatorAPICmd(out, errOut), ) return cmd diff --git a/kubectl-hlf/cmd/operatorapi/update.go b/kubectl-hlf/cmd/operatorapi/update.go new file mode 100644 index 00000000..21e8937e --- /dev/null +++ b/kubectl-hlf/cmd/operatorapi/update.go @@ -0,0 +1,131 @@ +package operatorapi + +import ( + "context" + "fmt" + "github.com/kfsoftware/hlf-operator/api/hlf.kungfusoftware.es/v1alpha1" + "github.com/kfsoftware/hlf-operator/kubectl-hlf/cmd/helpers" + "github.com/pkg/errors" + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "io" + "k8s.io/api/networking/v1beta1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type updateCmd struct { + out io.Writer + errOut io.Writer + apiOpts Options +} + +func (c *updateCmd) validate() error { + return c.apiOpts.Validate() +} +func (c *updateCmd) run() error { + oclient, err := helpers.GetKubeOperatorClient() + if err != nil { + return err + } + hosts := []v1alpha1.IngressHost{} + for _, host := range c.apiOpts.Hosts { + hosts = append(hosts, v1alpha1.IngressHost{ + Paths: []v1alpha1.IngressPath{ + { + Path: "/", + PathType: "Prefix", + }, + }, + Host: host, + }) + } + ingress := v1alpha1.Ingress{ + Enabled: true, + ClassName: "", + Annotations: map[string]string{ + "kubernetes.io/ingress.class": c.apiOpts.IngressClassName, + }, + TLS: []v1beta1.IngressTLS{}, + Hosts: hosts, + } + if c.apiOpts.TLSSecretName != "" { + ingress.TLS = []v1beta1.IngressTLS{ + { + Hosts: c.apiOpts.Hosts, + SecretName: c.apiOpts.TLSSecretName, + }, + } + } + config := v1alpha1.FabricOperatorAPIHLFConfig{ + MSPID: c.apiOpts.MSPID, + User: c.apiOpts.User, + NetworkConfig: v1alpha1.FabricOperatorAPINetworkConfig{ + SecretName: c.apiOpts.HLFSecretName, + Key: c.apiOpts.HLFKey, + }, + } + auth := &v1alpha1.FabricOperatorAPIAuth{ + OIDCJWKS: c.apiOpts.OIDCJWKS, + OIDCIssuer: c.apiOpts.OIDCIssuer, + } + ctx := context.Background() + fabricAPI, err := oclient.HlfV1alpha1().FabricOperatorAPIs(c.apiOpts.NS).Get(ctx, c.apiOpts.Name, v1.GetOptions{}) + if err != nil { + return errors.Wrapf(err, "failed to get Fabric Operator UI %s", c.apiOpts.Name) + } + fabricAPI.Spec.Ingress = ingress + fabricAPI.Spec.Image = c.apiOpts.Image + fabricAPI.Spec.Tag = c.apiOpts.Version + fabricAPI.Spec.HLFConfig = config + fabricAPI.Spec.Auth = auth + fabricAPI.Spec.Replicas = c.apiOpts.Replicas + if c.apiOpts.Output { + ot, err := helpers.MarshallWithoutStatus(&fabricAPI) + if err != nil { + return err + } + fmt.Println(string(ot)) + } else { + ctx := context.Background() + _, err = oclient.HlfV1alpha1().FabricOperatorAPIs(c.apiOpts.NS).Update( + ctx, + fabricAPI, + v1.UpdateOptions{}, + ) + if err != nil { + return err + } + log.Infof("Operator API %s created on namespace %s", fabricAPI.Name, fabricAPI.Namespace) + } + return nil +} +func newUpdateOperatorAPICmd(out io.Writer, errOut io.Writer) *cobra.Command { + c := updateCmd{out: out, errOut: errOut} + cmd := &cobra.Command{ + Use: "update", + Short: "Update a Operator API", + RunE: func(cmd *cobra.Command, args []string) error { + if err := c.validate(); err != nil { + return err + } + return c.run() + }, + } + f := cmd.Flags() + f.StringVar(&c.apiOpts.Name, "name", "", "Name of the Operator API to update") + f.StringVarP(&c.apiOpts.NS, "namespace", "n", helpers.DefaultNamespace, "Namespace scope for this request") + f.StringVarP(&c.apiOpts.Image, "image", "", helpers.DefaultOperationsOperatorAPIImage, "Image of the Operator API") + f.StringVarP(&c.apiOpts.Version, "version", "", helpers.DefaultOperationsOperatorAPIVersion, "Version of the Operator API") + f.StringVarP(&c.apiOpts.TLSSecretName, "tls-secret-name", "", "", "TLS Secret for the Operator API") + f.StringVarP(&c.apiOpts.IngressClassName, "ingress-class-name", "", "istio", "Ingress class name") + f.StringVarP(&c.apiOpts.MSPID, "hlf-mspid", "", "", "HLF Network Config MSPID") + f.StringVarP(&c.apiOpts.User, "hlf-user", "", "", "HLF Network Config User") + f.IntVarP(&c.apiOpts.Replicas, "replicas", "", 1, "Number of replicas of the Operator UI") + f.StringVarP(&c.apiOpts.HLFSecretName, "hlf-secret", "", "", "HLF Network Config Secret name") + f.StringVarP(&c.apiOpts.HLFKey, "hlf-secret-key", "", "", "HLF Network Config Secret key") + f.StringVarP(&c.apiOpts.OIDCJWKS, "oidc-jwks", "", "", "OIDC JWKS URL") + f.StringVarP(&c.apiOpts.OIDCIssuer, "oidc-issuer", "", "", "OIDC Issuer URL") + f.StringArrayVarP(&c.apiOpts.Hosts, "hosts", "", []string{}, "External hosts") + f.BoolVarP(&c.apiOpts.Output, "output", "o", false, "Output in yaml") + return cmd +} diff --git a/kubectl-hlf/cmd/operatorui/create.go b/kubectl-hlf/cmd/operatorui/create.go index be08ac5f..05666fd5 100644 --- a/kubectl-hlf/cmd/operatorui/create.go +++ b/kubectl-hlf/cmd/operatorui/create.go @@ -23,12 +23,20 @@ type Options struct { TLSSecretName string APIURL string IngresClassName string + LogoURL string + OIDCAuthority string + OIDCClientId string + OIDCScope string + Replicas int } func (o Options) Validate() error { if o.APIURL == "" { return fmt.Errorf("--api-url is required") } + if o.Replicas < 1 { + return fmt.Errorf("--replicas must be greater than 0") + } return nil } @@ -89,12 +97,18 @@ func (c *createCmd) run() error { Limits: nil, Requests: nil, }, + LogoURL: c.uiOpts.LogoURL, + Auth: &v1alpha1.FabricOperatorUIAuth{ + OIDCAuthority: c.uiOpts.OIDCAuthority, + OIDCClientId: c.uiOpts.OIDCClientId, + OIDCScope: c.uiOpts.OIDCScope, + }, APIURL: c.uiOpts.APIURL, Image: c.uiOpts.Image, Tag: c.uiOpts.Version, ImagePullPolicy: "Always", Tolerations: []corev1.Toleration{}, - Replicas: 1, + Replicas: c.uiOpts.Replicas, Env: []corev1.EnvVar{}, ImagePullSecrets: []corev1.LocalObjectReference{}, Affinity: &corev1.Affinity{}, @@ -144,5 +158,10 @@ func newCreateOperatorUICmd(out io.Writer, errOut io.Writer) *cobra.Command { f.StringVarP(&c.uiOpts.APIURL, "api-url", "", "", "API URL for the Operator UI") f.StringArrayVarP(&c.uiOpts.Hosts, "hosts", "", []string{}, "External hosts") f.BoolVarP(&c.uiOpts.Output, "output", "o", false, "Output in yaml") + f.IntVarP(&c.uiOpts.Replicas, "replicas", "", 1, "Number of replicas of the Operator UI") + f.StringVarP(&c.uiOpts.LogoURL, "logo-url", "", "", "Logo URL for the Operator UI") + f.StringVarP(&c.uiOpts.OIDCAuthority, "oidc-authority", "", "", "OIDC Authority for the Operator UI") + f.StringVarP(&c.uiOpts.OIDCClientId, "oidc-client-id", "", "", "OIDC Client ID for the Operator UI") + f.StringVarP(&c.uiOpts.OIDCScope, "oidc-scope", "", "", "OIDC Scope for the Operator UI") return cmd } diff --git a/kubectl-hlf/cmd/operatorui/operatorui.go b/kubectl-hlf/cmd/operatorui/operatorui.go index 40763165..03dae1c9 100644 --- a/kubectl-hlf/cmd/operatorui/operatorui.go +++ b/kubectl-hlf/cmd/operatorui/operatorui.go @@ -12,6 +12,7 @@ func NewOperatorUICMD(out io.Writer, errOut io.Writer) *cobra.Command { cmd.AddCommand( newCreateOperatorUICmd(out, errOut), newDeleteOperatorUICmd(out, errOut), + newUpdateOperatorUICmd(out, errOut), ) return cmd diff --git a/kubectl-hlf/cmd/operatorui/update.go b/kubectl-hlf/cmd/operatorui/update.go new file mode 100644 index 00000000..98c1c2cf --- /dev/null +++ b/kubectl-hlf/cmd/operatorui/update.go @@ -0,0 +1,123 @@ +package operatorui + +import ( + "context" + "fmt" + "github.com/kfsoftware/hlf-operator/api/hlf.kungfusoftware.es/v1alpha1" + "github.com/kfsoftware/hlf-operator/kubectl-hlf/cmd/helpers" + "github.com/pkg/errors" + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "io" + "k8s.io/api/networking/v1beta1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type updateCmd struct { + out io.Writer + errOut io.Writer + uiOpts Options +} + +func (c *updateCmd) validate() error { + return c.uiOpts.Validate() +} +func (c *updateCmd) run() error { + oclient, err := helpers.GetKubeOperatorClient() + if err != nil { + return err + } + hosts := []v1alpha1.IngressHost{} + for _, host := range c.uiOpts.Hosts { + hosts = append(hosts, v1alpha1.IngressHost{ + Paths: []v1alpha1.IngressPath{ + { + Path: "/", + PathType: "Prefix", + }, + }, + Host: host, + }) + } + ingress := v1alpha1.Ingress{ + Enabled: true, + ClassName: "", + Annotations: map[string]string{ + "kubernetes.io/ingress.class": c.uiOpts.IngresClassName, + }, + TLS: []v1beta1.IngressTLS{}, + Hosts: hosts, + } + if c.uiOpts.TLSSecretName != "" { + ingress.TLS = []v1beta1.IngressTLS{ + { + Hosts: c.uiOpts.Hosts, + SecretName: c.uiOpts.TLSSecretName, + }, + } + } + ctx := context.Background() + fabricOperatorUI, err := oclient.HlfV1alpha1().FabricOperatorUIs(c.uiOpts.NS).Get(ctx, c.uiOpts.Name, v1.GetOptions{}) + if err != nil { + return errors.Wrapf(err, "failed to get Fabric Operator UI %s", c.uiOpts.Name) + } + fabricOperatorUI.Spec.Image = c.uiOpts.Image + fabricOperatorUI.Spec.Tag = c.uiOpts.Version + fabricOperatorUI.Spec.Auth = &v1alpha1.FabricOperatorUIAuth{ + OIDCAuthority: c.uiOpts.OIDCAuthority, + OIDCClientId: c.uiOpts.OIDCClientId, + OIDCScope: c.uiOpts.OIDCScope, + } + fabricOperatorUI.Spec.LogoURL = c.uiOpts.LogoURL + fabricOperatorUI.Spec.Ingress = ingress + fabricOperatorUI.Spec.APIURL = c.uiOpts.APIURL + fabricOperatorUI.Spec.Replicas = c.uiOpts.Replicas + if c.uiOpts.Output { + ot, err := helpers.MarshallWithoutStatus(&fabricOperatorUI) + if err != nil { + return err + } + fmt.Println(string(ot)) + } else { + ctx := context.Background() + _, err = oclient.HlfV1alpha1().FabricOperatorUIs(c.uiOpts.NS).Update( + ctx, + fabricOperatorUI, + v1.UpdateOptions{}, + ) + if err != nil { + return err + } + log.Infof("Operator UI %s updated on namespace %s", fabricOperatorUI.Name, fabricOperatorUI.Namespace) + } + return nil +} +func newUpdateOperatorUICmd(out io.Writer, errOut io.Writer) *cobra.Command { + c := updateCmd{out: out, errOut: errOut} + cmd := &cobra.Command{ + Use: "update", + Short: "Update a Operator UI", + RunE: func(cmd *cobra.Command, args []string) error { + if err := c.validate(); err != nil { + return err + } + return c.run() + }, + } + f := cmd.Flags() + f.StringVar(&c.uiOpts.Name, "name", "", "Name of the Operator UI to update") + f.StringVarP(&c.uiOpts.NS, "namespace", "n", helpers.DefaultNamespace, "Namespace scope for this request") + f.StringVarP(&c.uiOpts.Image, "image", "", helpers.DefaultOperationsOperatorUIImage, "Image of the Operator UI") + f.StringVarP(&c.uiOpts.Version, "version", "", helpers.DefaultOperationsOperatorUIVersion, "Version of the Operator UI") + f.StringVarP(&c.uiOpts.IngresClassName, "ingress-class-name", "", "istio", "Ingress class name") + f.StringVarP(&c.uiOpts.TLSSecretName, "tls-secret-name", "", "", "TLS Secret for the Operator UI") + f.StringVarP(&c.uiOpts.APIURL, "api-url", "", "", "API URL for the Operator UI") + f.IntVarP(&c.uiOpts.Replicas, "replicas", "", 1, "Number of replicas of the Operator UI") + f.StringArrayVarP(&c.uiOpts.Hosts, "hosts", "", []string{}, "External hosts") + f.BoolVarP(&c.uiOpts.Output, "output", "o", false, "Output in yaml") + f.StringVarP(&c.uiOpts.LogoURL, "logo-url", "", "", "Logo URL for the Operator UI") + f.StringVarP(&c.uiOpts.OIDCAuthority, "oidc-authority", "", "", "OIDC Authority for the Operator UI") + f.StringVarP(&c.uiOpts.OIDCClientId, "oidc-client-id", "", "", "OIDC Client ID for the Operator UI") + f.StringVarP(&c.uiOpts.OIDCScope, "oidc-scope", "", "", "OIDC Scope for the Operator UI") + return cmd +} diff --git a/website-docs/docs/operator-ui/deploy-operator-ui.md b/website-docs/docs/operator-ui/deploy-operator-ui.md index c43fddd4..1502337d 100644 --- a/website-docs/docs/operator-ui/deploy-operator-ui.md +++ b/website-docs/docs/operator-ui/deploy-operator-ui.md @@ -13,6 +13,11 @@ export API_URL="http://api-operator./graphql" kubectl hlf operatorui create --name=operator-ui --namespace=default --hosts=$HOST --ingress-class-name=istio --api-url=$API_URL ``` +## Create operator UI with authentication + +```bash +``` + ## Delete operator UI In order to delete the operator UI: