blob: 901a33af95fd920aaaba93e841d978bb7d3a1a24 [file] [log] [blame]
package main
import (
"fmt"
"os"
"os/exec"
"path"
"strings"
"text/template"
)
import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
var cfg = config{}
type config struct {
name string
skipValidator bool
force bool
basePath string
gomodule string
version string
}
func (c config) policyPath() string {
return path.Join(c.basePath, c.lowercase())
}
func (c config) lowercase() string {
return strings.ToLower(c.name)
}
var rootCmd = &cobra.Command{
Use: "bootstrap",
Short: "Bootstrap a new policy",
RunE: func(cmd *cobra.Command, args []string) error {
if cfg.name == "" {
return errors.New("-name is required")
}
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "Bootstraping policy: %s at path %s\n", cfg.name, cfg.policyPath())
if !cfg.force {
_, err := os.Stat(cfg.policyPath())
if err == nil {
return fmt.Errorf("path %s already exists use -force to overwrite it", cfg.policyPath())
}
} else {
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "Deleting old policy code\n")
if err := os.RemoveAll(cfg.policyPath()); err != nil {
return err
}
}
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "Generating proto file\n")
if err := generateType(cfg); err != nil {
return err
}
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "Generating plugin file\n")
if err := generatePlugin(cfg); err != nil {
return err
}
path := fmt.Sprintf("generate/policy/%s", cfg.lowercase())
if err := exec.Command("make", path).Run(); err != nil {
return err
}
_, _ = cmd.OutOrStdout().Write([]byte(fmt.Sprintf(`
Successfully bootstrapped policy
regenerate auto generated files with: make generate/policy/%s
Useful files:
- %s the proto definition
- %s the validator
- %s the plugin implementation
`,
cfg.lowercase(),
fmt.Sprintf("%s/api/%s/%s.proto", cfg.policyPath(), cfg.version, cfg.lowercase()),
fmt.Sprintf("%s/api/%s/validator.go", cfg.policyPath(), cfg.version),
fmt.Sprintf("%s/plugin/%s/plugin.go", cfg.policyPath(), cfg.version),
)))
return nil
},
}
func generateType(c config) error {
apiPath := path.Join(c.policyPath(), "api", c.version)
if err := os.MkdirAll(apiPath, os.ModePerm); err != nil {
return err
}
f, err := os.Create(path.Join(apiPath, c.lowercase()+".go"))
if err != nil {
return err
}
err = typeTemplate.Execute(f, map[string]interface{}{
"name": c.name,
"nameLower": c.lowercase(),
"module": path.Join(c.gomodule, c.basePath),
"version": c.version,
})
if err != nil {
return err
}
if c.skipValidator {
return nil
}
f, err = os.Create(path.Join(apiPath, "validator.go"))
if err != nil {
return err
}
return validatorTemplate.Execute(f, map[string]interface{}{
"name": c.name,
"version": c.version,
})
}
func generatePlugin(c config) error {
dir := path.Join(c.policyPath(), "plugin", c.version)
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
return err
}
f, err := os.Create(path.Join(dir, "plugin.go"))
if err != nil {
return err
}
return pluginTemplate.Execute(f, map[string]interface{}{
"name": c.name,
"version": c.version,
"package": fmt.Sprintf("%s/%s/%s/api/%s", c.gomodule, c.basePath, c.lowercase(), c.version),
})
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func init() {
rootCmd.Flags().StringVar(&cfg.name, "name", "", "The name of the policy (UpperCamlCase)")
rootCmd.Flags().StringVar(&cfg.basePath, "path", "pkg/plugins/policies", "Where to put the generated code")
rootCmd.Flags().StringVar(&cfg.gomodule, "gomodule", "github.com/apache/dubbo-kubernetes", "Where to put the generated code")
rootCmd.Flags().StringVar(&cfg.version, "version", "v1alpha1", "The version to use")
rootCmd.Flags().BoolVar(&cfg.skipValidator, "skip-validator", false, "don't generator a validator empty file")
rootCmd.Flags().BoolVar(&cfg.force, "force", false, "Overwrite any existing code")
}
var typeTemplate = template.Must(template.New("").Option("missingkey=error").Parse(
`// +kubebuilder:object:generate=true
package {{ .version }}
// {{ .name }}
// +dubbo:policy:skip_registration=true
type {{ .name }} struct {
}
`))
var pluginTemplate = template.Must(template.New("").Option("missingkey=error").Parse(
`package {{ .version }}
import (
"github.com/apache/dubbo-kubernetes/pkg/core"
core_plugins "github.com/apache/dubbo-kubernetes/pkg/core/plugins"
core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
"github.com/apache/dubbo-kubernetes/pkg/plugins/policies/core/matchers"
api "{{ .package }}"
xds_context "github.com/apache/dubbo-kubernetes/pkg/xds/context"
)
var _ core_plugins.PolicyPlugin = &plugin{}
var log = core.Log.WithName("{{.name}}")
type plugin struct {
}
func NewPlugin() core_plugins.Plugin {
return &plugin{}
}
func (p plugin) MatchedPolicies(dataplane *core_mesh.DataplaneResource, resources xds_context.Resources) (core_xds.TypedMatchingPolicies, error) {
return matchers.MatchedPolicies(api.{{ .name }}Type, dataplane, resources)
}
func (p plugin) Apply(rs *core_xds.ResourceSet, ctx xds_context.Context, proxy *core_xds.Proxy) error {
log.Info("apply is not implemented")
return nil
}
`))
var validatorTemplate = template.Must(template.New("").Option("missingkey=error").Parse(
`package {{.version}}
import (
"github.com/apache/dubbo-kubernetes/pkg/core/validators"
)
func (r *{{.name}}Resource) validate() error {
var verr validators.ValidationError
return verr.OrNil()
}
`))