Skip to content

Commit 79fb55d

Browse files
authored
feat: optionally generate outputs for tf promise (#220)
1 parent 644ec71 commit 79fb55d

11 files changed

Lines changed: 290 additions & 129 deletions

File tree

cmd/add_container.go

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -348,15 +348,6 @@ func getContainerIdx(pipeline v1alpha1.Pipeline, containerName string) int {
348348
return -1
349349
}
350350

351-
func supportedLanguage(language string) bool {
352-
for _, sl := range supportedLanguages {
353-
if sl == language {
354-
return true
355-
}
356-
}
357-
return false
358-
}
359-
360351
func getTemplates(containerFileDirectory, containerScriptsDirectory, language string) map[string]string {
361352
pipelineScriptFilename := pipelineScriptFilename(language)
362353
pipelineScriptTemplateFilepath := fmt.Sprintf("templates/workflows/%s/%s.tpl", language, pipelineScriptFilename)

cmd/init_tf_module_promise.go

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -57,16 +57,24 @@ To pull modules from private registries, ensure your system is logged in to the
5757
--group syntasso.io \
5858
--kind IAM \
5959
--version v1alpha1`,
60-
RunE: InitFromTerraformModule,
60+
RunE: func(cmd *cobra.Command, args []string) error {
61+
if err := InitFromTerraformModule(cmd, args); err != nil {
62+
fmt.Printf("Error: %s\n", err)
63+
return err
64+
}
65+
return nil
66+
},
6167
Args: cobra.ExactArgs(1),
6268
}
6369

6470
moduleSource, moduleRegistryVersion string
6571
moduleProviders []string
72+
generateOutputs bool
6673
)
6774

6875
func init() {
6976
initCmd.AddCommand(terraformModuleCmd)
77+
terraformModuleCmd.SilenceUsage = true
7078
terraformModuleCmd.Flags().StringVarP(&moduleSource, "module-source", "s", "", "Source of the terraform module. \n"+
7179
"This can be a Git URL, Terraform registry path, or a local directory path. \n"+
7280
"It follows the same format as the `source` argument in the Terraform module block.",
@@ -77,33 +85,31 @@ func init() {
7785
terraformModuleCmd.Flags().StringSliceVarP(&moduleProviders, "module-providers", "", []string{}, "(Optional) the names of any files containing Terraform provider block; "+
7886
"defaults to versions.tf and providers.tf",
7987
)
88+
terraformModuleCmd.Flags().BoolVarP(&generateOutputs, "generate-outputs", "", false, "(Optional) generate Terraform 'output' blocks for the module")
8089
terraformModuleCmd.MarkFlagRequired("module-source")
8190
}
8291

8392
func InitFromTerraformModule(cmd *cobra.Command, args []string) error {
8493
fmt.Println("Fetching terraform module variables, this might take up to a minute...")
8594

8695
if moduleRegistryVersion != "" && !internal.IsTerraformRegistrySource(moduleSource) {
87-
fmt.Println("Error: --module-registry-version is only valid for Terraform registry sources like 'namespace/name/provider'. For git URLs (e.g., 'git::https://github.com/org/repo.git?ref=v1.0.0') or local paths, embed the ref directly in --module-source instead.")
96+
return fmt.Errorf("--module-registry-version is only valid for Terraform registry sources like 'namespace/name/provider'. For git URLs (e.g., 'git::https://github.com/org/repo.git?ref=v1.0.0') or local paths, embed the ref directly in --module-source instead.")
8897
}
8998

9099
moduleDir, err := internal.SetupModule(moduleSource, moduleRegistryVersion)
91100
if err != nil {
92-
fmt.Printf("Error: failed to setup module : %s\n", err)
93-
return nil
101+
return fmt.Errorf("failed to setup module: %w", err)
94102
}
95103
defer os.RemoveAll(moduleDir)
96104

97105
variables, err := internal.GetVariablesFromModule(moduleSource, moduleDir, moduleRegistryVersion)
98106
if err != nil {
99-
fmt.Printf("Error: failed to download and convert terraform module to CRD: %s\n", err)
100-
return nil
107+
return fmt.Errorf("failed to download and convert terraform module to CRD: %w", err)
101108
}
102109

103110
versionProviderFilepaths, err := internal.GetVersionsAndProvidersFromModule(moduleSource, moduleDir, moduleRegistryVersion, moduleProviders)
104111
if err != nil {
105-
fmt.Printf("Error: %s\n", err)
106-
return nil
112+
return fmt.Errorf("failed to get versions and providers from module: %w", err)
107113
}
108114

109115
crdSpecSchema, warnings := internal.VariablesToCRDSpecSchema(variables)
@@ -113,22 +119,27 @@ func InitFromTerraformModule(cmd *cobra.Command, args []string) error {
113119

114120
crdSchema, err := yaml.Marshal(crdSpecSchema)
115121
if err != nil {
116-
fmt.Printf("Error: failed to marshal CRD schema: %s\n", err)
117-
return nil
122+
return fmt.Errorf("failed to marshal CRD schema: %w", err)
123+
}
124+
125+
var moduleOutputNames []string
126+
if generateOutputs {
127+
moduleOutputNames, err = internal.GetOutputsFromModule(moduleDir)
128+
if err != nil {
129+
return fmt.Errorf("failed to extract module outputs: %w", err)
130+
}
118131
}
119132

120-
resourceConfigure, err := generateTerraformModuleResourceConfigurePipeline(moduleRegistryVersion)
133+
resourceConfigure, err := generateTerraformModuleResourceConfigurePipeline(moduleRegistryVersion, generateOutputs, moduleOutputNames)
121134
if err != nil {
122-
fmt.Printf("Error: failed to generate promise pipelines: %s\n", err)
123-
return nil
135+
return fmt.Errorf("failed to generate promise pipelines: %w", err)
124136
}
125137

126138
var promiseConfigure string
127139
if len(versionProviderFilepaths) > 1 {
128140
promiseConfigure, err = generateTerraformModulePromiseConfigurePipeline()
129141
if err != nil {
130-
fmt.Printf("Error: failed to generate promise configure pipelines: %s\n", err)
131-
return nil
142+
return fmt.Errorf("failed to generate promise configure pipelines: %w", err)
132143
}
133144
}
134145

@@ -137,10 +148,12 @@ func InitFromTerraformModule(cmd *cobra.Command, args []string) error {
137148
if moduleRegistryVersion != "" {
138149
extraFlags = fmt.Sprintf("%s --module-registry-version %s", extraFlags, moduleRegistryVersion)
139150
}
151+
if generateOutputs {
152+
extraFlags = fmt.Sprintf("%s --generate-outputs", extraFlags)
153+
}
140154
templateValues, err := generateTemplateValues(promiseName, "tf-module-promise", extraFlags, resourceConfigure, promiseConfigure, string(crdSchema))
141155
if err != nil {
142-
fmt.Printf("Error: failed to generate template values: %s\n", err)
143-
return nil
156+
return fmt.Errorf("failed to generate template values: %w", err)
144157
}
145158
templateValues.DestinationSelectors = "- matchLabels:\n environment: terraform"
146159

@@ -159,21 +172,19 @@ func InitFromTerraformModule(cmd *cobra.Command, args []string) error {
159172

160173
err = templateFiles(promiseTemplates, outputDir, templates, templateValues)
161174
if err != nil {
162-
fmt.Printf("Error: failed to template files: %s\n", err)
163-
return nil
175+
return fmt.Errorf("failed to template files: %w", err)
164176
}
165177

166178
err = writeDependencyFiles(versionProviderFilepaths)
167179
if err != nil {
168-
fmt.Printf("error writing promise dependencies: %s\n", err)
169-
return nil
180+
return fmt.Errorf("failed to write promise dependencies: %w", err)
170181
}
171182

172183
fmt.Println("Promise generated successfully. It is set to schedule to Destinations with the label `environment: terraform` by default. To modify this behavior, update the `.spec.destinationSelectors` field in `promise.yaml`")
173184
return nil
174185
}
175186

176-
func generateTerraformModuleResourceConfigurePipeline(moduleRegistryVersion string) (string, error) {
187+
func generateTerraformModuleResourceConfigurePipeline(moduleRegistryVersion string, generateOutputs bool, moduleOutputNames []string) (string, error) {
177188
envs := []corev1.EnvVar{
178189
{
179190
Name: "MODULE_SOURCE",
@@ -188,6 +199,13 @@ func generateTerraformModuleResourceConfigurePipeline(moduleRegistryVersion stri
188199
})
189200
}
190201

202+
if generateOutputs && len(moduleOutputNames) > 0 {
203+
envs = append(envs, corev1.EnvVar{
204+
Name: "MODULE_OUTPUT_NAMES",
205+
Value: strings.Join(moduleOutputNames, ","),
206+
})
207+
}
208+
191209
pipelines := []unstructured.Unstructured{
192210
{
193211
Object: map[string]any{

cmd/platform_get_resources.go

Lines changed: 1 addition & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,6 @@ var platformGetResourcesCmd = &cobra.Command{
4747
},
4848
}
4949

50-
var k8sQuerier Fetcher
51-
5250
func init() {
5351
platformGetCmd.AddCommand(platformGetResourcesCmd)
5452
}
@@ -98,7 +96,7 @@ func (q K8sQuerier) GetRequests(ctx context.Context, gvr *schema.GroupVersionRes
9896
if selector != "" {
9997
listOptions.LabelSelector = selector
10098
}
101-
promiseRequests, err := q.dynamicClient.Resource(*gvr).Namespace(v1.NamespaceAll).List(ctx, v1.ListOptions{})
99+
promiseRequests, err := q.dynamicClient.Resource(*gvr).Namespace(v1.NamespaceAll).List(ctx, listOptions)
102100
if err != nil {
103101
return nil, fmt.Errorf("error listing requests for %q: %w", promiseName, err)
104102
}
@@ -173,37 +171,6 @@ func clientsFromFlags(cf *genericclioptions.ConfigFlags) (K8sQuerier, error) {
173171
}, nil
174172
}
175173

176-
func listAllKratixGVRs(crdClient apiextensionsclient.Interface) ([]schema.GroupVersionResource, error) {
177-
ctx := context.Background()
178-
crds, err := crdClient.
179-
ApiextensionsV1().
180-
CustomResourceDefinitions().
181-
List(ctx, v1.ListOptions{LabelSelector: v1alpha1.PromiseNameLabel})
182-
if err != nil {
183-
return nil, fmt.Errorf("error listing promise CRDs: %w", err)
184-
}
185-
186-
var out []schema.GroupVersionResource
187-
for _, crd := range crds.Items {
188-
var storageVersion string
189-
for _, v := range crd.Spec.Versions {
190-
if v.Storage {
191-
storageVersion = v.Name
192-
break
193-
}
194-
}
195-
if storageVersion == "" {
196-
continue
197-
}
198-
out = append(out, schema.GroupVersionResource{
199-
Group: crd.Spec.Group,
200-
Version: storageVersion,
201-
Resource: crd.Spec.Names.Plural,
202-
})
203-
}
204-
return out, nil
205-
}
206-
207174
func initialiseQuerier(args []Fetcher) (Fetcher, error) {
208175
if len(args) == 0 {
209176
k8sQuerier, err := clientsFromFlags(configFlags)
@@ -289,33 +256,6 @@ func RenderTree(promiseName string, fetcher ...Fetcher) error {
289256
return nil
290257
}
291258

292-
func gvrForPromise(ctx context.Context, k8sClient client.Client, mapper meta.RESTMapper, promiseName string) (*schema.GroupVersionResource, error) {
293-
promise := &v1alpha1.Promise{}
294-
err := k8sClient.Get(ctx, types.NamespacedName{Name: promiseName}, promise)
295-
296-
if errors.IsNotFound(err) {
297-
return nil, fmt.Errorf("promise: %s not found", promiseName)
298-
}
299-
if client.IgnoreNotFound(err) != nil {
300-
return nil, fmt.Errorf("error getting promise: %s with error %q", promiseName, err)
301-
}
302-
303-
if !promise.ContainsAPI() {
304-
return nil, fmt.Errorf("promise: %s contains no API", promiseName)
305-
}
306-
307-
gvk, _, apiErr := promise.GetAPI()
308-
if apiErr != nil {
309-
return nil, fmt.Errorf("error generating GVK from promise: %s with error %q", promiseName, apiErr)
310-
}
311-
312-
gvr, err := buildGVR(mapper, gvk.Group, gvk.Version, gvk.Kind)
313-
if err != nil {
314-
return nil, fmt.Errorf("error generating GroupVersionResource: %q", err)
315-
}
316-
return &gvr, nil
317-
}
318-
319259
func buildGVR(mapper meta.RESTMapper, group, version, resource string) (schema.GroupVersionResource, error) {
320260
return mapper.ResourceFor(schema.GroupVersionResource{
321261
Group: group,

go.mod

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ require (
99
github.com/go-logr/stdr v1.2.2
1010
github.com/hashicorp/hcl/v2 v2.23.0
1111
github.com/mittwald/go-helm-client v0.12.10
12-
github.com/onsi/ginkgo/v2 v2.27.2
13-
github.com/onsi/gomega v1.38.2
12+
github.com/onsi/ginkgo/v2 v2.28.1
13+
github.com/onsi/gomega v1.39.0
1414
github.com/spf13/cobra v1.10.1
1515
github.com/syntasso/kratix v0.125.1-0.20250923144917-71691d914142
1616
github.com/zclconf/go-cty v1.13.0
@@ -68,15 +68,14 @@ require (
6868
github.com/google/btree v1.1.3 // indirect
6969
github.com/google/gnostic-models v0.6.9 // indirect
7070
github.com/google/go-cmp v0.7.0 // indirect
71-
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect
71+
github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83 // indirect
7272
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
7373
github.com/google/uuid v1.6.0 // indirect
7474
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
7575
github.com/gosuri/uitable v0.0.4 // indirect
7676
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
7777
github.com/hashicorp/errwrap v1.1.0 // indirect
7878
github.com/hashicorp/go-multierror v1.1.1 // indirect
79-
github.com/hashicorp/hcl v1.0.1-vault-7 // indirect
8079
github.com/huandu/xstrings v1.5.0 // indirect
8180
github.com/inconshreveable/mousetrap v1.1.0 // indirect
8281
github.com/jmoiron/sqlx v1.4.0 // indirect
@@ -121,17 +120,17 @@ require (
121120
github.com/xlab/treeprint v1.2.0 // indirect
122121
go.yaml.in/yaml/v2 v2.4.2 // indirect
123122
go.yaml.in/yaml/v3 v3.0.4 // indirect
124-
golang.org/x/crypto v0.45.0 // indirect
123+
golang.org/x/crypto v0.47.0 // indirect
125124
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa // indirect
126-
golang.org/x/mod v0.29.0 // indirect
127-
golang.org/x/net v0.47.0 // indirect
125+
golang.org/x/mod v0.32.0 // indirect
126+
golang.org/x/net v0.49.0 // indirect
128127
golang.org/x/oauth2 v0.30.0 // indirect
129-
golang.org/x/sync v0.18.0 // indirect
130-
golang.org/x/sys v0.38.0 // indirect
131-
golang.org/x/term v0.37.0 // indirect
132-
golang.org/x/text v0.31.0 // indirect
128+
golang.org/x/sync v0.19.0 // indirect
129+
golang.org/x/sys v0.40.0 // indirect
130+
golang.org/x/term v0.39.0 // indirect
131+
golang.org/x/text v0.33.0 // indirect
133132
golang.org/x/time v0.12.0 // indirect
134-
golang.org/x/tools v0.38.0 // indirect
133+
golang.org/x/tools v0.41.0 // indirect
135134
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
136135
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect
137136
google.golang.org/grpc v1.68.1 // indirect

0 commit comments

Comments
 (0)