This is an automated email from the ASF dual-hosted git repository.

pcongiusti pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-k.git


The following commit(s) were added to refs/heads/main by this push:
     new 31c66e7b4 fix(ci): bring back CLIs required by ci tasks
31c66e7b4 is described below

commit 31c66e7b4d30efdee76335f843575513cd31b263
Author: Pasquale Congiusti <pasquale.congiu...@gmail.com>
AuthorDate: Tue Jun 6 15:31:57 2023 +0200

    fix(ci): bring back CLIs required by ci tasks
    
    Closes #4460
---
 cmd/util/doc-gen/generators/generators.go       |  69 +++++
 cmd/util/doc-gen/generators/traitdocgen.go      | 328 ++++++++++++++++++++++++
 cmd/util/doc-gen/generators/traitmetadatagen.go | 183 +++++++++++++
 cmd/util/doc-gen/main.go                        |  47 ++++
 cmd/util/json-schema-gen/main.go                | 174 +++++++++++++
 cmd/util/license-check/main.go                  |  55 ++++
 cmd/util/platform-check/main.go                 |  68 +++++
 go.mod                                          |   3 +
 go.sum                                          |   4 +
 9 files changed, 931 insertions(+)

diff --git a/cmd/util/doc-gen/generators/generators.go 
b/cmd/util/doc-gen/generators/generators.go
new file mode 100644
index 000000000..09d66c501
--- /dev/null
+++ b/cmd/util/doc-gen/generators/generators.go
@@ -0,0 +1,69 @@
+/*
+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 generators
+
+import (
+       "path/filepath"
+       "strings"
+
+       "k8s.io/gengo/args"
+       "k8s.io/gengo/generator"
+       "k8s.io/gengo/namer"
+)
+
+// CustomArgs --.
+type CustomArgs struct {
+       DocDir      string
+       ResourceDir string
+       TraitPath   string
+       NavPath     string
+       ListPath    string
+}
+
+// NameSystems returns the name system used by the generators in this package.
+func NameSystems() namer.NameSystems {
+       return namer.NameSystems{
+               "default": namer.NewPublicNamer(0),
+       }
+}
+
+// DefaultNameSystem returns the default name system for ordering the types to 
be
+// processed by the generators in this package.
+func DefaultNameSystem() string {
+       return "default"
+}
+
+// Packages --.
+func Packages(context *generator.Context, arguments *args.GeneratorArgs) 
generator.Packages {
+       var packages generator.Packages
+       for _, i := range context.Inputs {
+               pkg := context.Universe[i]
+               if pkg == nil {
+                       continue
+               }
+
+               packages = append(packages, &generator.DefaultPackage{
+                       PackageName: strings.Split(filepath.Base(pkg.Path), 
".")[0],
+                       PackagePath: pkg.Path,
+                       GeneratorFunc: func(c *generator.Context) 
[]generator.Generator {
+                               return 
[]generator.Generator{NewTraitDocGen(arguments), NewtraitMetaDataGen(arguments)}
+                       },
+               })
+       }
+       return packages
+}
diff --git a/cmd/util/doc-gen/generators/traitdocgen.go 
b/cmd/util/doc-gen/generators/traitdocgen.go
new file mode 100644
index 000000000..08f8c69af
--- /dev/null
+++ b/cmd/util/doc-gen/generators/traitdocgen.go
@@ -0,0 +1,328 @@
+/*
+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 generators
+
+import (
+       "fmt"
+       "io"
+       "os"
+       "path"
+       "reflect"
+       "regexp"
+       "sort"
+       "strings"
+
+       "golang.org/x/text/cases"
+       "golang.org/x/text/language"
+
+       "k8s.io/gengo/args"
+       "k8s.io/gengo/generator"
+       "k8s.io/gengo/types"
+
+       v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1"
+       "github.com/apache/camel-k/v2/pkg/trait"
+       "github.com/apache/camel-k/v2/pkg/util"
+)
+
+const (
+       tagTrait = "+camel-k:trait"
+       tagLint  = "nolint"
+
+       adocCommonMarkerStart = "// Start of autogenerated code - DO NOT EDIT!"
+       adocCommonMarkerEnd   = "// End of autogenerated code - DO NOT EDIT!"
+
+       adocDescriptionMarkerStart = adocCommonMarkerStart + " (description)"
+       adocDescriptionMarkerEnd   = adocCommonMarkerEnd + " (description)"
+
+       adocConfigurationMarkerStart = adocCommonMarkerStart + " 
(configuration)"
+       adocConfigurationMarkerEnd   = adocCommonMarkerEnd + " (configuration)"
+
+       adocNavMarkerStart = adocCommonMarkerStart + " (trait-nav)"
+       adocNavMarkerEnd   = adocCommonMarkerEnd + " (trait-nav)"
+)
+
+var tagTraitID = regexp.MustCompile(fmt.Sprintf("%s=([a-z0-9-]+)", 
regexp.QuoteMeta(tagTrait)))
+
+// traitDocGen produces documentation about traits.
+type traitDocGen struct {
+       generator.DefaultGen
+       arguments           *args.GeneratorArgs
+       generatedTraitFiles []string
+}
+
+// traitDocGen implements Generator interface.
+var _ generator.Generator = &traitDocGen{}
+
+func NewTraitDocGen(arguments *args.GeneratorArgs) generator.Generator {
+       return &traitDocGen{
+               DefaultGen: generator.DefaultGen{},
+               arguments:  arguments,
+       }
+}
+
+func (g *traitDocGen) Filename() string {
+       return "zz_generated_doc.go"
+}
+
+func (g *traitDocGen) Filter(context *generator.Context, t *types.Type) bool {
+       for _, c := range t.CommentLines {
+               if strings.Contains(c, tagTrait) {
+                       return true
+               }
+       }
+       return false
+}
+
+func (g *traitDocGen) GenerateType(context *generator.Context, t *types.Type, 
out io.Writer) error {
+       customArgs, ok := g.arguments.CustomArgs.(*CustomArgs)
+       if !ok {
+               return fmt.Errorf("type assertion failed: %v", 
g.arguments.CustomArgs)
+       }
+       docDir := customArgs.DocDir
+       traitPath := customArgs.TraitPath
+       traitID := getTraitID(t)
+       traitFile := traitID + ".adoc"
+       filename := path.Join(docDir, traitPath, traitFile)
+
+       g.generatedTraitFiles = append(g.generatedTraitFiles, traitFile)
+
+       return util.WithFileContent(filename, func(file *os.File, data []byte) 
error {
+               content := strings.Split(string(data), "\n")
+
+               writeTitle(traitID, &content)
+               writeDescription(t, traitID, &content)
+               writeFields(t, traitID, &content)
+
+               return writeFile(file, content)
+       })
+}
+
+func (g *traitDocGen) Finalize(c *generator.Context, w io.Writer) error {
+       return g.FinalizeNav(c)
+}
+
+func (g *traitDocGen) FinalizeNav(*generator.Context) error {
+       customArgs, ok := g.arguments.CustomArgs.(*CustomArgs)
+       if !ok {
+               return fmt.Errorf("type assertion failed: %v", 
g.arguments.CustomArgs)
+       }
+       docDir := customArgs.DocDir
+       navPath := customArgs.NavPath
+       filename := path.Join(docDir, navPath)
+
+       return util.WithFileContent(filename, func(file *os.File, data []byte) 
error {
+               content := strings.Split(string(data), "\n")
+
+               pre, post := split(content, adocNavMarkerStart, 
adocNavMarkerEnd)
+
+               content = append([]string(nil), pre...)
+               content = append(content, adocNavMarkerStart)
+               sort.Strings(g.generatedTraitFiles)
+               for _, t := range g.generatedTraitFiles {
+                       name := traitNameFromFile(t)
+                       content = append(content, "** 
xref:traits:"+t+"["+name+"]")
+               }
+               content = append(content, adocNavMarkerEnd)
+               content = append(content, post...)
+
+               return writeFile(file, content)
+       })
+}
+
+func traitNameFromFile(file string) string {
+       name := strings.TrimSuffix(file, ".adoc")
+       name = strings.ReplaceAll(name, "trait", "")
+       name = strings.ReplaceAll(name, "-", " ")
+       name = strings.Trim(name, " ")
+       name = cases.Title(language.English).String(name)
+       return name
+}
+
+func writeTitle(traitID string, content *[]string) {
+       res := append([]string(nil), *content...)
+       for _, s := range res {
+               if strings.HasPrefix(s, "= ") {
+                       // Already has a title
+                       return
+               }
+       }
+       res = append([]string{"= " + 
cases.Title(language.English).String(strings.ReplaceAll(traitID, "-", " ")) + " 
Trait"}, res...)
+       *content = res
+}
+
+func writeDescription(t *types.Type, traitID string, content *[]string) {
+       pre, post := split(*content, adocDescriptionMarkerStart, 
adocDescriptionMarkerEnd)
+       res := append([]string(nil), pre...)
+       res = append(res, adocDescriptionMarkerStart)
+       res = append(res, filterOutTagsAndComments(t.CommentLines)...)
+       profiles := strings.Join(determineProfiles(traitID), ", ")
+       res = append(res, "", fmt.Sprintf("This trait is available in the 
following profiles: **%s**.", profiles))
+       if isPlatformTrait(traitID) {
+               res = append(res, "", fmt.Sprintf("WARNING: The %s trait is a 
*platform trait*: disabling it may compromise the platform functionality.", 
traitID))
+       }
+       res = append(res, "", adocDescriptionMarkerEnd)
+       res = append(res, post...)
+       *content = res
+}
+
+func writeFields(t *types.Type, traitID string, content *[]string) {
+       pre, post := split(*content, adocConfigurationMarkerStart, 
adocConfigurationMarkerEnd)
+       res := append([]string(nil), pre...)
+       res = append(res, adocConfigurationMarkerStart, "== Configuration", "")
+       res = append(res, "Trait properties can be specified when running any 
integration with the CLI:")
+       res = append(res, "[source,console]")
+       res = append(res, "----")
+       if len(t.Members) > 1 {
+               res = append(res, fmt.Sprintf("$ kamel run --trait 
%s.[key]=[value] --trait %s.[key2]=[value2] integration.groovy", traitID, 
traitID))
+       } else {
+               res = append(res, fmt.Sprintf("$ kamel run --trait 
%s.[key]=[value] integration.groovy", traitID))
+       }
+       res = append(res, "----")
+       res = append(res, "The following configuration options are available:", 
"")
+       res = append(res, "[cols=\"2m,1m,5a\"]", "|===")
+       res = append(res, "|Property | Type | Description", "")
+       writeMembers(t, traitID, &res)
+       res = append(res, "|===", "", adocConfigurationMarkerEnd)
+       res = append(res, post...)
+       *content = res
+}
+
+func writeMembers(t *types.Type, traitID string, content *[]string) {
+       res := append([]string(nil), *content...)
+       for _, m := range t.Members {
+               prop := reflect.StructTag(m.Tags).Get("property")
+               if prop == "" {
+                       continue
+               }
+
+               if strings.Contains(prop, "squash") {
+                       writeMembers(m.Type, traitID, &res)
+               } else {
+                       res = append(res, "| "+traitID+"."+prop)
+                       res = append(res, "| 
"+strings.TrimPrefix(m.Type.Name.Name, "*"))
+                       first := true
+                       for _, l := range 
filterOutTagsAndComments(m.CommentLines) {
+                               escapedComment := escapeASCIIDoc(l)
+                               if first {
+                                       res = append(res, "| "+escapedComment)
+                                       first = false
+                               } else {
+                                       res = append(res, escapedComment)
+                               }
+                       }
+                       res = append(res, "")
+               }
+       }
+       *content = res
+}
+
+func getTraitID(t *types.Type) string {
+       for _, s := range t.CommentLines {
+               if strings.Contains(s, tagTrait) {
+                       matches := tagTraitID.FindStringSubmatch(s)
+                       if len(matches) < 2 {
+                               panic(fmt.Sprintf("unable to extract trait ID 
from tag line `%s`", s))
+                       }
+                       return matches[1]
+               }
+       }
+       panic(fmt.Sprintf("trait ID not found in type %s", t.Name.Name))
+}
+
+func filterOutTagsAndComments(comments []string) []string {
+       res := make([]string, 0, len(comments))
+       for _, l := range comments {
+               if !strings.HasPrefix(strings.TrimLeft(l, " \t"), "+") &&
+                       !strings.HasPrefix(strings.TrimLeft(l, " \t"), "TODO:") 
&&
+                       !strings.HasPrefix(strings.TrimLeft(l, " \t"), tagLint) 
{
+                       res = append(res, l)
+               }
+       }
+       return res
+}
+
+// escapeAsciiDoc is in charge to escape those chars used for formatting 
purposes.
+func escapeASCIIDoc(text string) string {
+       return strings.ReplaceAll(text, "|", "\\|")
+}
+
+func split(doc []string, startMarker, endMarker string) ([]string, []string) {
+       if len(doc) == 0 {
+               return nil, nil
+       }
+       idx := len(doc)
+       for i, s := range doc {
+               if s == startMarker {
+                       idx = i
+                       break
+               }
+       }
+       idy := len(doc)
+       for j, s := range doc {
+               if j > idx && s == endMarker {
+                       idy = j
+                       break
+               }
+       }
+       pre := doc[0:idx]
+       post := []string{}
+       if idy < len(doc) {
+               post = doc[idy+1:]
+       }
+       return pre, post
+}
+
+func writeFile(file *os.File, content []string) error {
+       if err := file.Truncate(0); err != nil {
+               return err
+       }
+       max := 0
+       for i, line := range content {
+               if line != "" {
+                       max = i
+               }
+       }
+       for i, line := range content {
+               if i <= max {
+                       if _, err := file.WriteString(line + "\n"); err != nil {
+                               return err
+                       }
+               }
+       }
+       return nil
+}
+
+func isPlatformTrait(traitID string) bool {
+       catalog := trait.NewCatalog(nil)
+       t := catalog.GetTrait(traitID)
+       return t.IsPlatformTrait()
+}
+
+func determineProfiles(traitID string) []string {
+       var profiles []string
+       catalog := trait.NewCatalog(nil)
+       for _, p := range v1.AllTraitProfiles {
+               traits := catalog.TraitsForProfile(p)
+               for _, t := range traits {
+                       if string(t.ID()) == traitID {
+                               profiles = append(profiles, string(p))
+                       }
+               }
+       }
+       return profiles
+}
diff --git a/cmd/util/doc-gen/generators/traitmetadatagen.go 
b/cmd/util/doc-gen/generators/traitmetadatagen.go
new file mode 100644
index 000000000..5d3f3e0ab
--- /dev/null
+++ b/cmd/util/doc-gen/generators/traitmetadatagen.go
@@ -0,0 +1,183 @@
+/*
+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 generators
+
+import (
+       "fmt"
+       "io"
+       "os"
+       "path"
+       "reflect"
+       "sort"
+       "strings"
+
+       "github.com/apache/camel-k/v2/pkg/util"
+
+       "gopkg.in/yaml.v2"
+       "k8s.io/gengo/args"
+       "k8s.io/gengo/generator"
+       "k8s.io/gengo/types"
+)
+
+const traitFile = "traits.yaml"
+
+// traitMetaDataGen produces YAML documentation about trait descriptions.
+type traitMetaDataGen struct {
+       generator.DefaultGen
+       arguments *args.GeneratorArgs
+       Root      *traitMetaDataRoot
+}
+
+type traitMetaDataRoot struct {
+       Traits []traitMetaData `yaml:"traits"`
+}
+
+type traitMetaData struct {
+       Name        string                  `yaml:"name"`
+       Platform    bool                    `yaml:"platform"`
+       Profiles    []string                `yaml:"profiles"`
+       Description string                  `yaml:"description"`
+       Properties  []traitPropertyMetaData `yaml:"properties"`
+}
+
+type traitPropertyMetaData struct {
+       Name        string `yaml:"name"`
+       TypeName    string `yaml:"type"`
+       Description string `yaml:"description"`
+}
+
+// traitMetaDataGen implements Generator interface.
+var _ generator.Generator = &traitMetaDataGen{}
+
+// NewtraitMetaDataGen --.
+func NewtraitMetaDataGen(arguments *args.GeneratorArgs) generator.Generator {
+       return &traitMetaDataGen{
+               DefaultGen: generator.DefaultGen{},
+               arguments:  arguments,
+               Root:       &traitMetaDataRoot{},
+       }
+}
+
+func (g *traitMetaDataGen) Filename() string {
+       return "zz_desc_generated.go"
+}
+
+func (g *traitMetaDataGen) Filter(context *generator.Context, t *types.Type) 
bool {
+       for _, c := range t.CommentLines {
+               if strings.Contains(c, tagTrait) {
+                       return true
+               }
+       }
+       return false
+}
+
+func (g *traitMetaDataGen) GenerateType(context *generator.Context, t 
*types.Type, out io.Writer) error {
+       traitID := g.getTraitID(t)
+       td := &traitMetaData{}
+       g.buildDescription(t, traitID, td)
+       g.buildFields(t, td)
+       g.Root.Traits = append(g.Root.Traits, *td)
+       return nil
+}
+
+func (g *traitMetaDataGen) Finalize(c *generator.Context, w io.Writer) error {
+       customArgs, ok := g.arguments.CustomArgs.(*CustomArgs)
+       if !ok {
+               return fmt.Errorf("type assertion failed: %v", 
g.arguments.CustomArgs)
+       }
+       deployDir := customArgs.ResourceDir
+       filename := path.Join(deployDir, traitFile)
+
+       // reorder the traits metadata so that it always gets the identical 
result
+       sort.Slice(g.Root.Traits, func(i, j int) bool {
+               return g.Root.Traits[i].Name < g.Root.Traits[j].Name
+       })
+
+       return util.WithFile(filename, os.O_RDWR|os.O_CREATE, 0o777, func(file 
*os.File) error {
+               if err := file.Truncate(0); err != nil {
+                       return err
+               }
+
+               data, err := yaml.Marshal(g.Root)
+               if err != nil {
+                       fmt.Fprintf(file, "error: %v", err)
+               }
+               fmt.Fprintf(file, "%s", string(data))
+
+               return nil
+       })
+}
+
+func (g *traitMetaDataGen) getTraitID(t *types.Type) string {
+       for _, s := range t.CommentLines {
+               if strings.Contains(s, tagTrait) {
+                       matches := tagTraitID.FindStringSubmatch(s)
+                       if len(matches) < 2 {
+                               panic(fmt.Sprintf("unable to extract trait ID 
from tag line `%s`", s))
+                       }
+                       return matches[1]
+               }
+       }
+       panic(fmt.Sprintf("trait ID not found in type %s", t.Name.Name))
+}
+
+func (g *traitMetaDataGen) buildDescription(t *types.Type, traitID string, td 
*traitMetaData) {
+       desc := []string(nil)
+       desc = append(desc, filterOutTagsAndComments(t.CommentLines)...)
+       td.Name = traitID
+       td.Description = ""
+       for _, line := range desc {
+               text := strings.Trim(line, " ")
+               if len(text) == 0 {
+                       continue
+               }
+               if len(td.Description) > 0 {
+                       td.Description += " "
+               }
+               td.Description += text
+       }
+       td.Profiles = determineProfiles(traitID)
+       td.Platform = isPlatformTrait(traitID)
+}
+
+func (g *traitMetaDataGen) buildFields(t *types.Type, td *traitMetaData) {
+       if len(t.Members) > 1 {
+               res := []string(nil)
+               g.buildMembers(t, &res, td)
+       }
+}
+
+func (g *traitMetaDataGen) buildMembers(t *types.Type, content *[]string, td 
*traitMetaData) {
+       for _, m := range t.Members {
+               res := append([]string(nil), *content...)
+               prop := reflect.StructTag(m.Tags).Get("property")
+               if prop != "" {
+                       if strings.Contains(prop, "squash") {
+                               g.buildMembers(m.Type, &res, td)
+                       } else {
+                               pd := traitPropertyMetaData{}
+                               pd.Name = prop
+                               pd.TypeName = 
strings.TrimPrefix(m.Type.Name.Name, "*")
+
+                               res = append(res, 
filterOutTagsAndComments(m.CommentLines)...)
+                               pd.Description = strings.Join(res, " ")
+                               td.Properties = append(td.Properties, pd)
+                       }
+               }
+       }
+}
diff --git a/cmd/util/doc-gen/main.go b/cmd/util/doc-gen/main.go
new file mode 100644
index 000000000..b80d8bc54
--- /dev/null
+++ b/cmd/util/doc-gen/main.go
@@ -0,0 +1,47 @@
+/*
+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 main
+
+import (
+       "github.com/apache/camel-k/v2/cmd/util/doc-gen/generators"
+       "github.com/spf13/pflag"
+       "k8s.io/gengo/args"
+
+       _ "github.com/apache/camel-k/v2/addons"
+)
+
+func main() {
+       arguments := args.Default()
+
+       // Custom args.
+       customArgs := &generators.CustomArgs{}
+       pflag.CommandLine.StringVar(&customArgs.DocDir, "doc-dir", "./docs", 
"Root of the document directory.")
+       pflag.CommandLine.StringVar(&customArgs.ResourceDir, "resource-dir", 
"./resources", "Root of the resource directory.")
+       pflag.CommandLine.StringVar(&customArgs.TraitPath, "traits-path", 
"modules/traits/pages", "Path to the traits directory.")
+       pflag.CommandLine.StringVar(&customArgs.NavPath, "nav-path", 
"modules/ROOT/nav.adoc", "Path to the navigation file.")
+       pflag.CommandLine.StringVar(&customArgs.ListPath, "list-path", 
"modules/traits/pages/traits.adoc", "Path to the trait list file.")
+       arguments.CustomArgs = customArgs
+
+       if err := arguments.Execute(
+               generators.NameSystems(),
+               generators.DefaultNameSystem(),
+               generators.Packages,
+       ); err != nil {
+               panic(err)
+       }
+}
diff --git a/cmd/util/json-schema-gen/main.go b/cmd/util/json-schema-gen/main.go
new file mode 100644
index 000000000..d5ebe78cd
--- /dev/null
+++ b/cmd/util/json-schema-gen/main.go
@@ -0,0 +1,174 @@
+/*
+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 main
+
+import (
+       "encoding/json"
+       "fmt"
+       "os"
+       "reflect"
+       "strings"
+
+       "github.com/apache/camel-k/v2/pkg/util"
+
+       apiextensionsv1 
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+       clientscheme "k8s.io/client-go/kubernetes/scheme"
+
+       "github.com/apache/camel-k/v2/pkg/util/kubernetes"
+)
+
+func main() {
+       if len(os.Args) != 6 {
+               fmt.Fprintln(os.Stderr, `Use "json-schema-gen <crd> <schema> 
<path> <isArray> <destination>`)
+               os.Exit(1)
+       }
+       crd := os.Args[1]
+       schema := os.Args[2]
+       path := os.Args[3]
+       isArray := os.Args[4] == "true"
+       destination := os.Args[5]
+
+       if err := generate(crd, schema, path, isArray, destination); err != nil 
{
+               panic(err)
+       }
+}
+
+func generate(crdFilename, dslFilename, path string, isArray bool, destination 
string) error {
+       dslSchema, err := loadDslSchema(dslFilename)
+       if err != nil {
+               return err
+       }
+       if !isArray && dslSchema["type"] == "array" {
+               // nolint: forcetypeassert
+               dslSchema = dslSchema["items"].(map[string]interface{})
+       }
+
+       rebaseRefs(dslSchema)
+
+       bytes, err := json.Marshal(dslSchema)
+       if err != nil {
+               return err
+       }
+       schema := apiextensionsv1.JSONSchemaProps{}
+       err = json.Unmarshal(bytes, &schema)
+       if err != nil {
+               return err
+       }
+
+       crdSchema, err := loadCrdSchema(crdFilename)
+       if err != nil {
+               return err
+       }
+       // relocate definitions
+       if len(crdSchema.Definitions) > 0 {
+               panic("unexpected definitions found in CRD")
+       }
+       if isArray {
+               crdSchema.Definitions = schema.Items.Schema.Definitions
+               schema.Items.Schema.Definitions = 
apiextensionsv1.JSONSchemaDefinitions{}
+       } else {
+               crdSchema.Definitions = schema.Definitions
+               schema.Definitions = apiextensionsv1.JSONSchemaDefinitions{}
+       }
+
+       // merge DSL schema into the CRD schema
+       ref := *crdSchema
+       paths := pathComponents(path)
+       for _, p := range paths[:len(paths)-1] {
+               ref = ref.Properties[p]
+       }
+       ref.Properties[paths[len(paths)-1]] = schema
+
+       result, err := json.MarshalIndent(crdSchema, "", "  ")
+       if err != nil {
+               return err
+       }
+       return os.WriteFile(destination, result, 0o600)
+}
+
+func remapRef(ref string) string {
+       return "#" + strings.TrimPrefix(ref, "#/items")
+}
+
+func rebaseRefs(schema map[string]interface{}) {
+       for k, v := range schema {
+               switch {
+               case k == "$ref" && reflect.TypeOf(v).Kind() == reflect.String:
+                       schema[k] = remapRef(fmt.Sprintf("%v", v))
+               case reflect.TypeOf(v).Kind() == reflect.Map:
+                       if m, ok := v.(map[string]interface{}); ok {
+                               rebaseRefs(m)
+                       }
+               case reflect.TypeOf(v).Kind() == reflect.Slice:
+                       if vs, ok := v.([]interface{}); ok {
+                               for _, vv := range vs {
+                                       if reflect.TypeOf(vv).Kind() == 
reflect.Map {
+                                               if m, ok := 
vv.(map[string]interface{}); ok {
+                                                       rebaseRefs(m)
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
+func loadDslSchema(filename string) (map[string]interface{}, error) {
+       bytes, err := util.ReadFile(filename)
+       if err != nil {
+               return nil, err
+       }
+       var dslSchema map[string]interface{}
+       if err := json.Unmarshal(bytes, &dslSchema); err != nil {
+               return nil, err
+       }
+       return dslSchema, nil
+}
+
+func loadCrdSchema(filename string) (*apiextensionsv1.JSONSchemaProps, error) {
+       bytes, err := util.ReadFile(filename)
+       if err != nil {
+               return nil, err
+       }
+       scheme := clientscheme.Scheme
+       err = apiextensionsv1.AddToScheme(scheme)
+       if err != nil {
+               return nil, err
+       }
+       obj, err := kubernetes.LoadResourceFromYaml(scheme, string(bytes))
+       if err != nil {
+               return nil, err
+       }
+       crd, ok := obj.(*apiextensionsv1.CustomResourceDefinition)
+       if !ok {
+               return nil, fmt.Errorf("type assertion failed: %v", obj)
+       }
+
+       return crd.Spec.Versions[0].Schema.OpenAPIV3Schema, nil
+}
+
+func pathComponents(path string) []string {
+       res := make([]string, 0)
+       for _, p := range strings.Split(path, ".") {
+               if len(strings.TrimSpace(p)) == 0 {
+                       continue
+               }
+               res = append(res, p)
+       }
+       return res
+}
diff --git a/cmd/util/license-check/main.go b/cmd/util/license-check/main.go
new file mode 100644
index 000000000..6d79257a6
--- /dev/null
+++ b/cmd/util/license-check/main.go
@@ -0,0 +1,55 @@
+/*
+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 main
+
+import (
+       "fmt"
+       "os"
+       "strings"
+
+       "github.com/apache/camel-k/v2/pkg/util"
+)
+
+func main() {
+       if len(os.Args) != 3 {
+               fmt.Fprintln(os.Stderr, `Use "license-check <file> <license>`)
+               os.Exit(1)
+       }
+
+       fileName := os.Args[1]
+       licenseName := os.Args[2]
+
+       fileBin, err := util.ReadFile(fileName)
+       if err != nil {
+               _, _ = fmt.Fprintf(os.Stderr, "cannot read file %s: %v\n", 
fileName, err)
+               os.Exit(1)
+       }
+       file := string(fileBin)
+
+       licenseBin, err := util.ReadFile(licenseName)
+       if err != nil {
+               _, _ = fmt.Fprintf(os.Stderr, "cannot read file %s: %v\n", 
licenseName, err)
+               os.Exit(1)
+       }
+       license := string(licenseBin)
+
+       if !strings.Contains(file, license) {
+               _, _ = fmt.Fprintf(os.Stderr, "file %s does not contain 
license\n", fileName)
+               os.Exit(1)
+       }
+}
diff --git a/cmd/util/platform-check/main.go b/cmd/util/platform-check/main.go
new file mode 100644
index 000000000..4b052b279
--- /dev/null
+++ b/cmd/util/platform-check/main.go
@@ -0,0 +1,68 @@
+/*
+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 main
+
+import (
+       "fmt"
+       "os"
+       "strings"
+
+       "k8s.io/client-go/kubernetes"
+       "k8s.io/client-go/rest"
+       "sigs.k8s.io/controller-runtime/pkg/client/config"
+)
+
+func main() {
+       fmt.Println(platformID())
+}
+
+func platformID() string {
+       client := APIClient()
+       _, apiResourceLists, err := 
client.Discovery().ServerGroupsAndResources()
+       exitOnError(err)
+
+       for _, apiResList := range apiResourceLists {
+               // Should be version independent just in case image api is ever 
upgraded
+               if strings.Contains(apiResList.GroupVersion, 
"image.openshift.io") {
+                       return "openshift"
+               }
+       }
+
+       return "kubernetes"
+}
+
+func exitOnError(err error) {
+       if err != nil {
+               fmt.Println("ERROR:", err)
+               os.Exit(1)
+       }
+}
+
+func RestConfig() *rest.Config {
+       restConfig, err := config.GetConfig()
+       exitOnError(err)
+
+       return restConfig
+}
+
+func APIClient() kubernetes.Interface {
+       apiClient, err := kubernetes.NewForConfig(RestConfig())
+       exitOnError(err)
+
+       return apiClient
+}
diff --git a/go.mod b/go.mod
index ae87e876c..25f68480a 100644
--- a/go.mod
+++ b/go.mod
@@ -52,6 +52,7 @@ require (
        k8s.io/apimachinery v0.25.6
        k8s.io/cli-runtime v0.25.6
        k8s.io/client-go v0.25.6
+       k8s.io/gengo v0.0.0-20221011193443-fad74ee6edd9
        k8s.io/klog/v2 v2.100.1
        k8s.io/kubectl v0.25.6
        k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2
@@ -139,8 +140,10 @@ require (
        go.opencensus.io v0.24.0 // indirect
        go.uber.org/atomic v1.9.0 // indirect
        golang.org/x/crypto v0.9.0 // indirect
+       golang.org/x/mod v0.10.0 // indirect
        golang.org/x/net v0.10.0 // indirect
        golang.org/x/sys v0.8.0 // indirect
+       golang.org/x/tools v0.9.1 // indirect
        gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
        google.golang.org/api v0.122.0 // indirect
        google.golang.org/appengine v1.6.7 // indirect
diff --git a/go.sum b/go.sum
index e7846c52a..df85a30f0 100644
--- a/go.sum
+++ b/go.sum
@@ -822,6 +822,7 @@ golang.org/x/mod v0.4.0/go.mod 
h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
+golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod 
h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod 
h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod 
h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1070,6 +1071,7 @@ golang.org/x/tools v0.1.0/go.mod 
h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
 golang.org/x/tools v0.1.2/go.mod 
h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.5/go.mod 
h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo=
+golang.org/x/tools v0.9.1/go.mod 
h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1279,6 +1281,8 @@ k8s.io/component-base v0.25.6 
h1:v3ci6FbXFcxpjyQJaaLq0MgzT3vyFzwUDWtO+KRv9Bk=
 k8s.io/component-base v0.25.6/go.mod 
h1:k7DfcfJ8cOI6A2xTCfU5LxsnXV+lWw1ME8cRCHzIh6o=
 k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod 
h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
 k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod 
h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
+k8s.io/gengo v0.0.0-20221011193443-fad74ee6edd9 
h1:iu3o/SxaHVI7tKPtkGzD3M9IzrE21j+CUKH98NQJ8Ms=
+k8s.io/gengo v0.0.0-20221011193443-fad74ee6edd9/go.mod 
h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
 k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
 k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
 k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=

Reply via email to