Skip to content

Commit

Permalink
Refactor code to take into account optional fields
Browse files Browse the repository at this point in the history
Signed-off-by: Luke Mallon (Nalum) <[email protected]>
  • Loading branch information
Nalum committed Mar 3, 2024
1 parent 5f4dd1a commit 4e87baf
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 46 deletions.
22 changes: 11 additions & 11 deletions cmd/timoni/testdata/module/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,15 @@ timoni -n module delete module

## Configuration

| KEY | TYPE | DEFAULT | DESCRIPTION |
|------------------------------|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `metadata: labels:` | `struct` | `{"app.kubernetes.io/name": "module-name","app.kubernetes.io/kube": "1.27.5","app.kubernetes.io/version": "0.0.0-devel","app.kubernetes.io/team": "test"}` | Map of string keys and values that can be used to organize and categorize (scope and select) objects. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels Standard Kubernetes labels: app name and version. |
| `client: enabled:` | `bool` | `true` | |
| `client: image: repository:` | `string` | `"cgr.dev/chainguard/timoni"` | Repository is the address of a container registry repository. An image repository is made up of slash-separated name components, optionally prefixed by a registry hostname and port in the format [HOST[:PORT_NUMBER]/]PATH. |
| `client: image: tag:` | `string` | `"latest-dev"` | Tag identifies an image in the repository. A tag name may contain lowercase and uppercase characters, digits, underscores, periods and dashes. A tag name may not start with a period or a dash and may contain a maximum of 128 characters. |
| `client: image: digest:` | `string` | `"sha256:b49fbaac0eedc22c1cfcd26684707179cccbed0df205171bae3e1bae61326a10"` | Digest uniquely and immutably identifies an image in the repository. Spec: https://github.com/opencontainers/image-spec/blob/main/descriptor.md#digests. |
| `server: enabled:` | `bool` | `true` | |
| `domain:` | `string` | `"example.internal"` | |
| `ns: enabled:` | `bool` | `false` | |
| `team:` | `string` | `"test"` | |
| KEY | TYPE | DEFAULT | DESCRIPTION |
|------------------------------|----------|-----------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `metadata: annotations:` | `struct` | `{}` | Annotations is an unstructured key value map stored with a resource that may be set to store and retrieve arbitrary metadata. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations |
| `client: enabled:` | `bool` | `true` | |
| `client: image: repository:` | `string` | `"cgr.dev/chainguard/timoni"` | Repository is the address of a container registry repository. An image repository is made up of slash-separated name components, optionally prefixed by a registry hostname and port in the format [HOST[:PORT_NUMBER]/]PATH. |
| `client: image: tag:` | `string` | `"latest-dev"` | Tag identifies an image in the repository. A tag name may contain lowercase and uppercase characters, digits, underscores, periods and dashes. A tag name may not start with a period or a dash and may contain a maximum of 128 characters. |
| `client: image: digest:` | `string` | `"sha256:b49fbaac0eedc22c1cfcd26684707179cccbed0df205171bae3e1bae61326a10"` | Digest uniquely and immutably identifies an image in the repository. Spec: https://github.com/opencontainers/image-spec/blob/main/descriptor.md#digests. |
| `server: enabled:` | `bool` | `true` | |
| `domain:` | `string` | `"example.internal"` | |
| `ns: enabled:` | `bool` | `false` | |
| `team:` | `string` | `"test"` | |

108 changes: 73 additions & 35 deletions internal/engine/module_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -323,52 +323,90 @@ func (b *ModuleBuilder) GetConfigDoc(value cue.Value) ([][]string, error) {
return nil, fmt.Errorf("lookup %s failed: %w", apiv1.ConfigValuesSelector, cfgValues.Err())
}

labelDomain := regexp.MustCompile(`^([a-zA-Z0-9-_.]+)?(".+")?$`)
rows, err := iterateFields(cfgValues)
if err != nil {
return nil, err
}

return rows, nil
}

func iterateFields(v cue.Value) ([][]string, error) {
var rows [][]string
configDataInfo := func(v cue.Value) bool {
var row []string
var noDoc bool
var doc string

for _, d := range v.Doc() {
if line := len(d.List) - 1; line >= 0 {
switch d.List[line].Text {
case "// +nodoc":
noDoc = true
break
}

fields, err := v.Fields(
cue.Optional(true),
cue.Concrete(true),
cue.Docs(true),
)
if err != nil {
return nil, fmt.Errorf("Cue Fields Error: %w", err)
}

for fields.Next() {
v := fields.Value()

// We are chekcing if the field is a struct and not optional and is concrete before we iterate through it
// this allows for definition of default values as full structs without generating output for each
// field in the struct where it doesn't make sense e.g.
//
// - annotations?: {[string]: string}
// - affinity: corev1.Affinity | *{nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: [...]}
if v.IncompleteKind() == cue.StructKind && !fields.IsOptional() && v.IsConcrete() {
iRows, err := iterateFields(v)
if err != nil {
return nil, err
}

doc += d.Text()
doc = strings.ReplaceAll(doc, "\n", " ")
doc = strings.ReplaceAll(doc, "+required", "")
doc = strings.ReplaceAll(doc, "+optional", "")
rows = append(rows, iRows...)
} else {
rows = append(rows, getField(v))
}
}

if !noDoc {
defaultVal, _ := v.Default()
valueBytes, _ := defaultVal.MarshalJSON()
valueType := strings.ReplaceAll(v.IncompleteKind().String(), "|", "\\|")
return rows, nil
}

value := strings.ReplaceAll(string(valueBytes), "\":", "\": ")
value = strings.ReplaceAll(value, "\":[", "\": [")
value = strings.ReplaceAll(value, "},", "}, ")
value = strings.ReplaceAll(value, "|", "\\|")
func getField(v cue.Value) []string {
var row []string
labelDomain := regexp.MustCompile(`^([a-zA-Z0-9-_.]+)?(".+")?$`)

field := strings.Replace(v.Path().String(), "timoni.instance.config.", "", 1)
match := labelDomain.FindStringSubmatch(field)
var noDoc bool
var doc string

row = append(row, fmt.Sprintf("`%s:`", strings.ReplaceAll(match[1], ".", ": ")+match[2]))
row = append(row, fmt.Sprintf("`%s`", valueType))
row = append(row, fmt.Sprintf("`%s`", value))
row = append(row, fmt.Sprintf("%s", doc))
rows = append(rows, row)
for _, d := range v.Doc() {
if line := len(d.List) - 1; line >= 0 {
switch d.List[line].Text {
case "// +nodoc":
noDoc = true
break
}
}

return true
doc += d.Text()
doc = strings.ReplaceAll(doc, "\n", " ")
doc = strings.ReplaceAll(doc, "+required", "")
doc = strings.ReplaceAll(doc, "+optional", "")
}

cfgValues.Walk(configDataInfo, nil)
return rows, nil
if !noDoc {
defaultVal, _ := v.Default()
valueBytes, _ := defaultVal.MarshalJSON()
valueType := strings.ReplaceAll(v.IncompleteKind().String(), "|", "\\|")

value := strings.ReplaceAll(string(valueBytes), "\":", "\": ")
value = strings.ReplaceAll(value, "\":[", "\": [")
value = strings.ReplaceAll(value, "},", "}, ")
value = strings.ReplaceAll(value, "|", "\\|")

field := strings.Replace(v.Path().String(), "timoni.instance.config.", "", 1)
match := labelDomain.FindStringSubmatch(field)

row = append(row, fmt.Sprintf("`%s:`", strings.ReplaceAll(match[1], ".", ": ")+match[2]))
row = append(row, fmt.Sprintf("`%s`", valueType))
row = append(row, fmt.Sprintf("`%s`", value))
row = append(row, fmt.Sprintf("%s", doc))
}

return row
}

0 comments on commit 4e87baf

Please sign in to comment.