operator/cmd/cluster/manifest.go (124 lines of code) (raw):
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cluster
import (
"cmp"
"fmt"
"github.com/apache/dubbo-kubernetes/dubboctl/pkg/cli"
"github.com/apache/dubbo-kubernetes/operator/pkg/manifest"
"github.com/apache/dubbo-kubernetes/operator/pkg/render"
"github.com/apache/dubbo-kubernetes/operator/pkg/util/clog"
"github.com/apache/dubbo-kubernetes/pkg/kube"
"github.com/apache/dubbo-kubernetes/pkg/util/slices"
"github.com/spf13/cobra"
"sigs.k8s.io/yaml"
"strings"
)
type manifestGenerateArgs struct {
// filenames is an array of paths to input DubboOperator CR files.
// filenames []string
// sets is a string with the format "path=value".
sets []string
}
func (a *manifestGenerateArgs) String() string {
var b strings.Builder
// b.WriteString("filenames: " + fmt.Sprint(a.filenames) + "\n")
b.WriteString("sets: " + fmt.Sprint(a.sets) + "\n")
return b.String()
}
func addManifestGenerateFlags(cmd *cobra.Command, args *manifestGenerateArgs) {
// cmd.PersistentFlags().StringSliceVarP(&args.filenames, "filenames", "f", nil, ``)
cmd.PersistentFlags().StringArrayVarP(&args.sets, "set", "s", nil, `Override dubboOperator values, such as selecting profiles, etc.`)
}
func ManifestCmd(ctx cli.Context) *cobra.Command {
rootArgs := &RootArgs{}
mgcArgs := &manifestGenerateArgs{}
mgc := manifestGenerateCmd(ctx, rootArgs, mgcArgs)
mc := &cobra.Command{
Use: "manifest",
Short: "dubbo manifest related commands",
Long: "The manifest command will generates dubbo manifests.",
}
AddFlags(mc, rootArgs)
AddFlags(mgc, rootArgs)
addManifestGenerateFlags(mgc, mgcArgs)
mc.AddCommand(mgc)
return mc
}
var kubeClientFunc func() (kube.CLIClient, error)
func manifestGenerateCmd(ctx cli.Context, _ *RootArgs, mgArgs *manifestGenerateArgs) *cobra.Command {
return &cobra.Command{
Use: "generate",
Short: "Generates an Dubbo install manifest",
Long: "The generate subcommand generates an Dubbo install manifest and outputs to the console by default.",
Example: ` # Generate a default Dubbo installation
dubboctl manifest generate
# Generate the demo profile
dubboctl manifest generate --set profile=demo
`,
RunE: func(cmd *cobra.Command, args []string) error {
if kubeClientFunc == nil {
kubeClientFunc = ctx.CLIClient
}
var kubeClient kube.CLIClient
kc, err := kubeClientFunc()
if err != nil {
return err
}
kubeClient = kc
cl := clog.NewConsoleLogger(cmd.OutOrStdout(), cmd.ErrOrStderr())
return manifestGenerate(kubeClient, mgArgs, cl)
},
}
}
const (
YAMLSeparator = "\n---\n"
)
func manifestGenerate(kc kube.CLIClient, mgArgs *manifestGenerateArgs, cl clog.Logger) error {
setFlags := applyFlagAliases(mgArgs.sets)
manifests, _, err := render.GenerateManifest(nil, setFlags, cl, kc)
if err != nil {
return err
}
for _, smf := range sortManifests(manifests) {
cl.Print(smf + YAMLSeparator)
}
return nil
}
func sortManifests(raw []manifest.ManifestSet) []string {
all := []manifest.Manifest{}
for _, m := range raw {
all = append(all, m.Manifests...)
}
slices.SortStableFunc(all, func(a, b manifest.Manifest) int {
if r := cmp.Compare(objectKindOrder(a), objectKindOrder(b)); r != 0 {
return r
}
if r := cmp.Compare(a.GroupVersionKind().Group, b.GroupVersionKind().Group); r != 0 {
return r
}
if r := cmp.Compare(a.GroupVersionKind().Kind, b.GroupVersionKind().Kind); r != 0 {
return r
}
return cmp.Compare(a.GetName(), b.GetName())
})
return slices.Map(all, func(e manifest.Manifest) string {
res, _ := yaml.Marshal(e.Object)
return string(res)
})
}
func objectKindOrder(m manifest.Manifest) int {
o := m.Unstructured
gk := o.GroupVersionKind().Group + "/" + o.GroupVersionKind().Kind
switch {
// Create CRDs asap - both because they are slow and because we will likely create instances of them soon
case gk == "apiextensions.k8s.io/CustomResourceDefinition":
return -1000
// We need to create ServiceAccounts, Roles before we bind them with a RoleBinding
case gk == "/ServiceAccount" || gk == "rbac.authorization.k8s.io/ClusterRole":
return 1
case gk == "rbac.authorization.k8s.io/ClusterRoleBinding":
return 2
// Pods might need configmap or secrets - avoid backoff by creating them first
case gk == "/ConfigMap" || gk == "/Secrets":
return 100
// Create the pods after we've created other things they might be waiting for
case gk == "extensions/Deployment" || gk == "apps/Deployment":
return 1000
// Autoscalers typically act on a deployment
case gk == "autoscaling/HorizontalPodAutoscaler":
return 1001
// Create services late - after pods have been started
case gk == "/Service":
return 10000
default:
return 1000
}
}