github.com/crossplane-contrib/function-cue@v0.2.2-0.20240508161918-5100fcb5a058/cmd/fn-cue-tools/tools.go (about) 1 // Licensed to Elasticsearch B.V. under one or more contributor 2 // license agreements. See the NOTICE file distributed with 3 // this work for additional information regarding copyright 4 // ownership. Elasticsearch B.V. licenses this file to you under 5 // the Apache License, Version 2.0 (the "License"); you may 6 // not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, 12 // software distributed under the License is distributed on an 13 // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 // KIND, either express or implied. See the License for the 15 // specific language governing permissions and limitations 16 // under the License. 17 18 package main 19 20 import ( 21 "fmt" 22 "io" 23 "os" 24 "path/filepath" 25 26 "github.com/crossplane-contrib/function-cue/internal/cuetools" 27 "github.com/pkg/errors" 28 "github.com/spf13/cobra" 29 ) 30 31 func checkNoArgs(cmd *cobra.Command, args []string) error { 32 if len(args) > 0 { 33 return fmt.Errorf("no arguments are expected for this command: found %v", args) 34 } 35 cmd.SilenceUsage = true 36 return nil 37 } 38 39 func checkOneArg(cmd *cobra.Command, args []string) error { 40 if len(args) != 1 { 41 return fmt.Errorf("exactly one argument is expected for this command, got %d, %v", len(args), args) 42 } 43 cmd.SilenceUsage = true 44 return nil 45 } 46 47 func writeFile(file string, content []byte) error { 48 dir := filepath.Dir(file) 49 if err := os.MkdirAll(dir, 0o755); err != nil { 50 return err 51 } 52 return os.WriteFile(file, content, 0o644) 53 } 54 55 func openapiCommand() *cobra.Command { 56 var pkg, outFile string 57 c := &cobra.Command{ 58 Use: "openapi ./path/to/package/dir", 59 Short: "generate self-contained openapi schemas for cue types from a cue package", 60 RunE: func(cmd *cobra.Command, args []string) error { 61 if err := checkOneArg(cmd, args); err != nil { 62 return err 63 } 64 out, err := cuetools.GenerateOpenAPISchema(args[0], pkg) 65 if err != nil { 66 return errors.Wrap(err, "generate schemas") 67 } 68 if outFile == "" || outFile == "-" { 69 fmt.Println(string(out)) 70 return nil 71 } 72 return writeFile(outFile, out) 73 }, 74 } 75 f := c.Flags() 76 f.StringVar(&pkg, "pkg", "schemas", "package name of generated cue file") 77 f.StringVar(&outFile, "out-file", "", "output file name, default is stdout") 78 return c 79 } 80 81 func packageScriptCommand() *cobra.Command { 82 var pkg, outFile, out, varName string 83 c := &cobra.Command{ 84 Use: "package-script ./path/to/package/dir", 85 Short: "generate a self-contained script as text", 86 RunE: func(cmd *cobra.Command, args []string) error { 87 if err := checkOneArg(cmd, args); err != nil { 88 return err 89 } 90 out, err := cuetools.PackageScript(args[0], cuetools.PackageScriptOpts{ 91 VarName: varName, 92 OutputPackage: pkg, 93 Format: cuetools.OutputFormat(out), 94 }) 95 if err != nil { 96 return errors.Wrap(err, "generate schemas") 97 } 98 if outFile == "" || outFile == "-" { 99 fmt.Println(string(out)) 100 return nil 101 } 102 return writeFile(outFile, out) 103 }, 104 } 105 f := c.Flags() 106 f.StringVar(&pkg, "pkg", "", "package name of generated cue file") 107 f.StringVar(&varName, "var", "_script", "the variable name to use for the script, cue format only") 108 f.StringVar(&outFile, "out-file", "", "output file name, default is stdout") 109 f.StringVarP(&out, "output", "o", string(cuetools.FormatCue), "output format, one of cue or raw") 110 return c 111 } 112 113 func extractSchemaCommand() *cobra.Command { 114 var pkg, file, outFile string 115 c := &cobra.Command{ 116 Use: "extract-schema", 117 Short: "extract a cue schema from the openAPI spec of a CRD/XRD object", 118 RunE: func(cmd *cobra.Command, args []string) error { 119 if err := checkNoArgs(cmd, args); err != nil { 120 return err 121 } 122 var reader io.Reader 123 if file == "" || file == "-" { 124 reader = os.Stdin 125 } else { 126 f, err := os.Open(file) 127 if err != nil { 128 return err 129 } 130 defer func() { _ = f.Close() }() 131 reader = f 132 } 133 out, err := cuetools.ExtractSchema(reader, pkg) 134 if err != nil { 135 return errors.Wrap(err, "generate schemas") 136 } 137 if outFile == "" || outFile == "-" { 138 fmt.Println(string(out)) 139 return nil 140 } 141 return writeFile(outFile, out) 142 }, 143 } 144 f := c.Flags() 145 f.StringVar(&pkg, "pkg", "", "package name of generated cue file") 146 f.StringVar(&file, "file", "-", "input JSON or YAML file containing a single CRD/ XRD definition, defaults to stdin") 147 f.StringVar(&outFile, "out-file", "", "output file name, defaults to stdout") 148 return c 149 } 150 151 func cueTestCommand() *cobra.Command { 152 var p cuetools.TestConfig 153 c := &cobra.Command{ 154 Use: "cue-test ./path/to/package/dir", 155 Short: "run unit tests for your composition implementation", 156 RunE: func(cmd *cobra.Command, args []string) error { 157 if err := checkOneArg(cmd, args); err != nil { 158 return err 159 } 160 p.Package = args[0] 161 tester, err := cuetools.NewTester(p) 162 if err != nil { 163 return err 164 } 165 return tester.Run() 166 }, 167 } 168 f := c.Flags() 169 f.StringSliceVar(&p.TestTags, "tag", nil, "list of test tags to enable, one per test") 170 f.StringVar(&p.TestPackage, "test-dir", "", "relative path to test package, defaults to a tests subdirectory under the package") 171 f.StringVar(&p.RequestVar, "request", "#request", "request variable to inject") 172 f.StringVar(&p.ResponseVar, "response", "response", "response variable to extract") 173 f.BoolVar(&p.LegacyDesiredOnlyResponse, "legacy-response", false, "enable legacy response") 174 f.BoolVar(&p.Debug, "debug", false, "enable eval debugging") 175 return c 176 }