Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement terraform support #8

Merged
merged 15 commits into from
Sep 5, 2023
Merged

implement terraform support #8

merged 15 commits into from
Sep 5, 2023

Conversation

nabuskey
Copy link
Contributor

@nabuskey nabuskey commented Aug 9, 2023

This became a large PR because I had to fix issues caused by merge conflict in the previous PR. Changes to CRDs introduced in this PR are supposed to be there in the main branch to begin with.

The meat of the PR is TF stuff.

Supports two workflows. For example variables given by this:

variable "name" {
  description = "Name of the VPC and EKS Cluster"
  type        = string
  default     = "emr-eks-ack"
}

# This lacks the default value, therefore it is a required field. 
variable "region" {
  description = "Region"
  type        = string
}
  1. Generation of fields only. Meaning it allows you to create a file per module for relevant field values only. Output for the above input would be:
properties:
  name:
    type: string
    description: Name of the VPC and EKS Cluster
    default: emr-eks-ack
  region:
    type: string
    description: Region
required:
  - region
  1. Generate then insert these fields to an object specified by json path in a template. For example, if the specified jsonpath is .spec.parameters[0] and the given template looks like this.
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
spec:
  parameters:
    - title: Choose Resource
      description: Select a AWS resource to add to your repository.
      properties:
        path:
          type: string
          description: path to place this file into
          default: kustomize/base
        name:
          type: string
          description: na

The output is

apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
spec:
  parameters:
    - title: Choose Resource
      description: Select a AWS resource to add to your repository.
      properties:
        path:
          type: string
          description: path to place this file into
          default: kustomize/base
        name:
          type: string
          description: Name of the VPC and EKS Cluster # Overwritten by variables value. 
          default: emr-eks-ack
        region:
          type: string
          description: Region
      required: 
        - region   

Current supported behaviour is merge keys and if duplicate keys are present, the ones in variables take precedent.

@nabuskey nabuskey requested a review from nimakaviani August 9, 2023 23:37
pkg/cmd/terraform.go Outdated Show resolved Hide resolved
func init() {
tfCmd.Flags().Uint32Var(&depth, "depth", 2, "depth from given directory to search for TF modules")
tfCmd.Flags().StringVarP(&templatePath, "templatePath", "t", "scaffolding/template.yaml", "path to the template to be augmented with backstage info")
tfCmd.Flags().StringVarP(&insertionPoint, "insertAt", "p", "", "jq path within the template to insert backstage info")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably requires more hints. also should it come with a default of placing it under spec.parameters[0] if empty?

I also wonder why we didnt take the same approach of adding a resource.enum list under the properties element and create separate yaml files for the terraform module added

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also why do we need a json path? can these params go anywhere other than under properties?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I considered it having a default value of spec.parameters[0] but I wanted default behaviour to just generate properties and requirements.

This approach allows you to have one template per module. I noticed having many different yaml files with oneOf property slows down form rendering in Backstage and defaults are not handled correctly.

json path is needed to insert these to wherever users want to insert it. It's possible to have nested objects in json schema form. So if they have a template and want to insert TF properties at spec.params[0].terraformInput, they can do so.

pkg/cmd/terraform.go Outdated Show resolved Hide resolved
pkg/cmd/terraform.go Outdated Show resolved Hide resolved
Comment on lines 214 to 240
return strings.ReplaceAll(input, " ", "")
}

func isPrimitive(s string) bool {
return s == "string" || s == "number" || s == "bool"
}

func isNestedPrimitive(s string) bool {
nested := strings.HasPrefix(s, "object(") || strings.HasPrefix(s, "map(") || strings.HasPrefix(s, "list(")
if nested {
return isPrimitive(getNestedType(s))
}
return false
}

func getNestedType(s string) string {
if strings.HasPrefix(s, "object(") {
return strings.TrimSuffix(strings.SplitAfterN(s, "object(", 1)[1], ")")
}
if strings.HasPrefix(s, "map(") {
return strings.TrimSuffix(strings.SplitAfterN(s, "map(", 2)[1], ")")
}
if strings.HasPrefix(s, "list(") {
return strings.TrimSuffix(strings.SplitAfterN(s, "list(", 2)[1], ")")
}
return s
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are these functions not included under utils.go? also if the functions in utils.go are only used by the terraform command, why are we splitting them out?

Copy link
Contributor Author

@nabuskey nabuskey Aug 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are meant for TF only for now. I think it's possible to re-use these for openapi but I really did not want to make changes to CRD cmd for this PR.

The functions in utils.go are meant to be used by others including crd cmd. I just didn't want to touch it for this.

description: VPC CIDR
default: 10.1.0.0/16
required:
- awsResources
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here


func init() {
tfCmd.Flags().Uint32Var(&depth, "depth", 2, "depth from given directory to search for TF modules")
tfCmd.Flags().StringVarP(&templatePath, "templatePath", "t", "scaffolding/template.yaml", "path to the template to be augmented with backstage info")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since we have this both for the CRD and TF, shall we push it under templateCmd?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think this is obscure unintuitive info that if template info is not provided, you will still get some parameters only output. shall we make it explicit by creating a separate flag that is xor to templatePath?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes that's what I wanted as a final product. But not for this PR.

. "github.com/onsi/gomega"
)

var _ = Describe("Terraform Template", func() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we test all the happy paths. can we test for some error verifications etc too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes we should.

pkg/cmd/terraform.go Outdated Show resolved Hide resolved
@@ -4,14 +4,13 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ugh, sucks that all these got overwritten.

Comment on lines 98 to 111
func getModules(inputDir string, currentDepth uint32) []string {
if currentDepth > depth {
return nil
}
if tfconfig.IsModuleDir(inputDir) {
return []string{inputDir}
}
base, _ := filepath.Abs(inputDir)
files, _ := os.ReadDir(base)
out := make([]string, 1)
for _, file := range files {
f := filepath.Join(base, file.Name())
mods := getModules(f, currentDepth+1)
out = append(out, mods...)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we combine this and func defs from the crd command with some neat closure and make them one function? look very similar structurally.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather not, this is intended for TF only and I don't want to have TF specific handling stuff in a common util file.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i thought the closure that we path to directory traversal would be command specific. so for tf and crd you handle things separately in their respective code. but the directory traversal bit can be generalized

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I refactored it and handle errors now.

What we look for is different in each case. In TF we look for directories, while we look for files in CRDs. So not much to have in the util file.

@nabuskey nabuskey linked an issue Aug 11, 2023 that may be closed by this pull request
@nabuskey
Copy link
Contributor Author

Refactored CRD cmd to use util functions. CRD cmd now allows for the same functionalities as terraform one.

  1. Generation of property files only. These are yaml files with properties key under which schema information is located. See: pkg/cmd/fakes/crd/valid/output/properties-awsblueprints.io.xcdn.yaml
  2. Original behaviour where all resources are pushed under a template. : pkg/cmd/fakes/crd/valid/output/full-template-oneof.yaml
  3. Generation of a template file per definition. pkg/cmd/fakes/crd/valid/output/full-template-awsblueprints.io.xcdn.yaml and pkg/cmd/fakes/crd/valid/output/full-template-sparkoperator.k8s.io.sparkapplication.yaml

@nimakaviani nimakaviani merged commit bc321d9 into main Sep 5, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Make the depth configurable for the CRD command
2 participants