blob: daf7a1a33f994476df31fad84dcd577b395af76f [file] [log] [blame]
package main
import (
"bytes"
"flag"
"go/format"
"log"
"os"
"sort"
"strings"
"text/template"
)
import (
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"
)
import (
_ "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
_ "github.com/apache/dubbo-kubernetes/api/system/v1alpha1"
. "github.com/apache/dubbo-kubernetes/tools/resource-gen/genutils"
)
// CustomResourceTemplate for creating a Kubernetes CRD to wrap a Dubbo resource.
var CustomResourceTemplate = template.Must(template.New("custom-resource").Parse(`
// Generated by tools/resource-gen
// Run "make generate" to update this file.
{{ $pkg := printf "%s_proto" .Package }}
{{ $tk := "` + "`" + `" }}
// nolint:whitespace
package v1alpha1
import (
"fmt"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
{{ $pkg }} "github.com/apache/dubbo-kubernetes/api/{{ .Package }}/v1alpha1"
core_model "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
"github.com/apache/dubbo-kubernetes/pkg/plugins/resources/k8s/native/pkg/model"
"github.com/apache/dubbo-kubernetes/pkg/plugins/resources/k8s/native/pkg/registry"
util_proto "github.com/apache/dubbo-kubernetes/pkg/util/proto"
)
{{range .Resources}}
{{- if not .SkipKubernetesWrappers }}
// +kubebuilder:object:root=true
{{- if .ScopeNamespace }}
// +kubebuilder:resource:categories=dubbo,scope=Namespaced
{{- else }}
// +kubebuilder:resource:categories=dubbo,scope=Cluster
{{- end}}
{{- range .AdditionalPrinterColumns }}
// +kubebuilder:printcolumn:{{ . }}
{{- end}}
type {{.ResourceType}} struct {
metav1.TypeMeta {{ $tk }}json:",inline"{{ $tk }}
metav1.ObjectMeta {{ $tk }}json:"metadata,omitempty"{{ $tk }}
// Mesh is the name of the dubbo mesh this resource belongs to.
// It may be omitted for cluster-scoped resources.
//
// +kubebuilder:validation:Optional
Mesh string {{ $tk }}json:"mesh,omitempty"{{ $tk }}
{{- if eq .ResourceType "DataplaneInsight" }}
// Status is the status the dubbo resource.
// +kubebuilder:validation:Optional
Status *apiextensionsv1.JSON {{ $tk }}json:"status,omitempty"{{ $tk }}
{{- else}}
// Spec is the specification of the Dubbo {{ .ProtoType }} resource.
// +kubebuilder:validation:Optional
Spec *apiextensionsv1.JSON {{ $tk }}json:"spec,omitempty"{{ $tk }}
{{- end}}
}
// +kubebuilder:object:root=true
{{- if .ScopeNamespace }}
// +kubebuilder:resource:scope=Cluster
{{- else }}
// +kubebuilder:resource:scope=Namespaced
{{- end}}
type {{.ResourceType}}List struct {
metav1.TypeMeta {{ $tk }}json:",inline"{{ $tk }}
metav1.ListMeta {{ $tk }}json:"metadata,omitempty"{{ $tk }}
Items []{{.ResourceType}} {{ $tk }}json:"items"{{ $tk }}
}
{{- if not .SkipRegistration}}
func init() {
SchemeBuilder.Register(&{{.ResourceType}}{}, &{{.ResourceType}}List{})
}
{{- end}}
func (cb *{{.ResourceType}}) GetObjectMeta() *metav1.ObjectMeta {
return &cb.ObjectMeta
}
func (cb *{{.ResourceType}}) SetObjectMeta(m *metav1.ObjectMeta) {
cb.ObjectMeta = *m
}
func (cb *{{.ResourceType}}) GetMesh() string {
return cb.Mesh
}
func (cb *{{.ResourceType}}) SetMesh(mesh string) {
cb.Mesh = mesh
}
func (cb *{{.ResourceType}}) GetSpec() (core_model.ResourceSpec, error) {
{{- if eq .ResourceType "DataplaneInsight" }}
spec := cb.Status
{{- else}}
spec := cb.Spec
{{- end}}
m := {{$pkg}}.{{.ProtoType}}{}
if spec == nil || len(spec.Raw) == 0 {
return &m, nil
}
err := util_proto.FromJSON(spec.Raw, &m)
return &m, err
}
func (cb *{{.ResourceType}}) SetSpec(spec core_model.ResourceSpec) {
if spec == nil {
{{- if eq .ResourceType "DataplaneInsight" }}
cb.Status = nil
{{- else }}
cb.Spec = nil
{{- end }}
return
}
s, ok := spec.(*{{$pkg}}.{{.ProtoType}});
if !ok {
panic(fmt.Sprintf("unexpected protobuf message type %T", spec))
}
{{ if eq .ResourceType "DataplaneInsight" }}
cb.Status = &apiextensionsv1.JSON{Raw: util_proto.MustMarshalJSON(s)}
{{- else}}
cb.Spec = &apiextensionsv1.JSON{Raw: util_proto.MustMarshalJSON(s)}
{{- end}}
}
func (cb *{{.ResourceType}}) Scope() model.Scope {
{{- if .ScopeNamespace }}
return model.ScopeNamespace
{{- else }}
return model.ScopeCluster
{{- end }}
}
func (l *{{.ResourceType}}List) GetItems() []model.KubernetesObject {
result := make([]model.KubernetesObject, len(l.Items))
for i := range l.Items {
result[i] = &l.Items[i]
}
return result
}
{{if not .SkipRegistration}}
func init() {
registry.RegisterObjectType(&{{ $pkg }}.{{.ProtoType}}{}, &{{.ResourceType}}{
TypeMeta: metav1.TypeMeta{
APIVersion: GroupVersion.String(),
Kind: "{{.ResourceType}}",
},
})
registry.RegisterListType(&{{ $pkg }}.{{.ProtoType}}{}, &{{.ResourceType}}List{
TypeMeta: metav1.TypeMeta{
APIVersion: GroupVersion.String(),
Kind: "{{.ResourceType}}List",
},
})
}
{{- end }} {{/* .SkipRegistration */}}
{{- end }} {{/* .SkipKubernetesWrappers */}}
{{- end }} {{/* Resources */}}
`))
// ResourceTemplate for creating a Dubbo resource.
var ResourceTemplate = template.Must(template.New("resource").Funcs(map[string]any{"hasSuffix": strings.HasSuffix, "trimSuffix": strings.TrimSuffix}).Parse(`
// Generated by tools/resource-gen.
// Run "make generate" to update this file.
{{ $pkg := printf "%s_proto" .Package }}
// nolint:whitespace
package {{.Package}}
import (
"fmt"
{{$pkg}} "github.com/apache/dubbo-kubernetes/api/{{.Package}}/v1alpha1"
"github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
"github.com/apache/dubbo-kubernetes/pkg/core/resources/registry"
)
{{range .Resources}}
{{ $baseType := trimSuffix (trimSuffix .ResourceType "Overview") "Insight" }}
const (
{{.ResourceType}}Type model.ResourceType = "{{.ResourceType}}"
)
var _ model.Resource = &{{.ResourceName}}{}
type {{.ResourceName}} struct {
Meta model.ResourceMeta
Spec *{{$pkg}}.{{.ProtoType}}
}
func New{{.ResourceName}}() *{{.ResourceName}} {
return &{{.ResourceName}}{
Spec: &{{$pkg}}.{{.ProtoType}}{},
}
}
func (t *{{.ResourceName}}) GetMeta() model.ResourceMeta {
return t.Meta
}
func (t *{{.ResourceName}}) SetMeta(m model.ResourceMeta) {
t.Meta = m
}
func (t *{{.ResourceName}}) GetSpec() model.ResourceSpec {
return t.Spec
}
{{with $in := .}}
{{range .Selectors}}
func (t *{{$in.ResourceName}}) {{.}}() []*{{$pkg}}.Selector {
return t.Spec.Get{{.}}()
}
{{end}}
{{end}}
func (t *{{.ResourceName}}) SetSpec(spec model.ResourceSpec) error {
protoType, ok := spec.(*{{$pkg}}.{{.ProtoType}})
if !ok {
return fmt.Errorf("invalid type %T for Spec", spec)
} else {
if protoType == nil {
t.Spec = &{{$pkg}}.{{.ProtoType}}{}
} else {
t.Spec = protoType
}
return nil
}
}
func (t *{{.ResourceName}}) Descriptor() model.ResourceTypeDescriptor {
return {{.ResourceName}}TypeDescriptor
}
{{- if and (hasSuffix .ResourceType "Overview") (ne $baseType "Service") }}
func (t *{{.ResourceName}}) SetOverviewSpec(resource model.Resource, insight model.Resource) error {
t.SetMeta(resource.GetMeta())
overview := &{{$pkg}}.{{.ProtoType}}{
{{$baseType}}: resource.GetSpec().(*{{$pkg}}.{{$baseType}}),
}
if insight != nil {
ins, ok := insight.GetSpec().(*{{$pkg}}.{{$baseType}}Insight)
if !ok {
return errors.New("failed to convert to insight type '{{$baseType}}Insight'")
}
overview.{{$baseType}}Insight = ins
}
return t.SetSpec(overview)
}
{{- end }}
var _ model.ResourceList = &{{.ResourceName}}List{}
type {{.ResourceName}}List struct {
Items []*{{.ResourceName}}
Pagination model.Pagination
}
func (l *{{.ResourceName}}List) GetItems() []model.Resource {
res := make([]model.Resource, len(l.Items))
for i, elem := range l.Items {
res[i] = elem
}
return res
}
func (l *{{.ResourceName}}List) GetItemType() model.ResourceType {
return {{.ResourceType}}Type
}
func (l *{{.ResourceName}}List) NewItem() model.Resource {
return New{{.ResourceName}}()
}
func (l *{{.ResourceName}}List) AddItem(r model.Resource) error {
if trr, ok := r.(*{{.ResourceName}}); ok {
l.Items = append(l.Items, trr)
return nil
} else {
return model.ErrorInvalidItemType((*{{.ResourceName}})(nil), r)
}
}
func (l *{{.ResourceName}}List) GetPagination() *model.Pagination {
return &l.Pagination
}
func (l *{{.ResourceName}}List) SetPagination(p model.Pagination) {
l.Pagination = p
}
var {{.ResourceName}}TypeDescriptor = model.ResourceTypeDescriptor{
Name: {{.ResourceType}}Type,
Resource: New{{.ResourceName}}(),
ResourceList: &{{.ResourceName}}List{},
ReadOnly: {{.WsReadOnly}},
AdminOnly: {{.WsAdminOnly}},
Scope: {{if .Global}}model.ScopeGlobal{{else}}model.ScopeMesh{{end}},
{{- if ne .DdsDirection ""}}
DDSFlags: {{.DdsDirection}},
{{- end}}
WsPath: "{{.WsPath}}",
DubboctlArg: "{{.DubboctlSingular}}",
DubboctlListArg: "{{.DubboctlPlural}}",
AllowToInspect: {{.AllowToInspect}},
IsPolicy: {{.IsPolicy}},
SingularDisplayName: "{{.SingularDisplayName}}",
PluralDisplayName: "{{.PluralDisplayName}}",
IsExperimental: {{.IsExperimental}},
}
{{- if not .SkipRegistration}}
func init() {
registry.RegisterType({{.ResourceName}}TypeDescriptor)
}
{{- end}}
{{end}}
`))
// ProtoMessageFunc ...
type ProtoMessageFunc func(protoreflect.MessageType) bool
// OnDubboResourceMessage ...
func OnDubboResourceMessage(pkg string, f ProtoMessageFunc) ProtoMessageFunc {
return func(m protoreflect.MessageType) bool {
r := DubboResourceForMessage(m.Descriptor())
if r == nil {
return true
}
if r.Package == pkg {
return f(m)
}
return true
}
}
func main() {
var gen string
var pkg string
flag.StringVar(&gen, "generator", "", "the type of generator to run options: (type,crd)")
flag.StringVar(&pkg, "package", "", "the name of the package to generate: (mesh, system)")
flag.Parse()
switch pkg {
case "mesh", "system":
default:
log.Fatalf("package %s is not supported", pkg)
}
var types []protoreflect.MessageType
protoregistry.GlobalTypes.RangeMessages(
OnDubboResourceMessage(pkg, func(m protoreflect.MessageType) bool {
types = append(types, m)
return true
}))
// Sort by name so the output is deterministic.
sort.Slice(types, func(i, j int) bool {
return types[i].Descriptor().FullName() < types[j].Descriptor().FullName()
})
var resources []ResourceInfo
for _, t := range types {
resourceInfo := ToResourceInfo(t.Descriptor())
resources = append(resources, resourceInfo)
}
var generatorTemplate *template.Template
switch gen {
case "type":
generatorTemplate = ResourceTemplate
case "crd":
generatorTemplate = CustomResourceTemplate
default:
log.Fatalf("%s is not a valid generator option\n", gen)
}
outBuf := bytes.Buffer{}
if err := generatorTemplate.Execute(&outBuf, struct {
Package string
Resources []ResourceInfo
}{
Package: pkg,
Resources: resources,
}); err != nil {
log.Fatalf("template error: %s", err)
}
out, err := format.Source(outBuf.Bytes())
if err != nil {
log.Fatalf("%s\n", err)
}
if _, err := os.Stdout.Write(out); err != nil {
log.Fatalf("%s\n", err)
}
}