Skip to content

Automatically generate type definitions for config files, make them always up-to-date and type-checked

License

Notifications You must be signed in to change notification settings

ufukty/gonfique

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

gonfique - Type checked configs for Go programs

Gonfique logo

gonfique is a CLI tool for Go developers to automatically build exact struct definitions in Go that will match the provided YAML or JSON config. Makes instant to notice when and where a breaking change occurs. Since compiler warns whenever it happens by type-checking, and source control shows where the change exactly is.

Example

Input:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-deployment
  namespace: my-namespace
type: Opaque
data:
  my-key: my-value
  password: cGFzc3dvcmQ=
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  rules:
    - host: myapp.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: my-service
                port:
                  number: 80
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
        - name: my-container
          image: my-image
          ports:
            - containerPort: 80
          envFrom:
            - configMapRef:
                name: my-config
            - secretRef:
                name: my-secret

Output:

package config

import (
  "fmt"
  "os"

  "gopkg.in/yaml.v3"
)

type Config struct {
  ApiVersion string `yaml:"apiVersion"`
  Data       struct {
    MyKey    string `yaml:"my-key"`
    Password string `yaml:"password"`
  } `yaml:"data"`
  Kind     string `yaml:"kind"`
  Metadata struct {
    Name      string `yaml:"name"`
    Namespace string `yaml:"namespace"`
  } `yaml:"metadata"`
  Spec struct {
    Ports []struct {
      Port       int    `yaml:"port"`
      Protocol   string `yaml:"protocol"`
      TargetPort int    `yaml:"targetPort"`
    } `yaml:"ports"`
    Replicas int `yaml:"replicas"`
    Rules    []struct {
      Host string `yaml:"host"`
      Http struct {
        Paths []struct {
          Backend struct {
            Service struct {
              Name string `yaml:"name"`
              Port struct {
                Number int `yaml:"number"`
              } `yaml:"port"`
            } `yaml:"service"`
          } `yaml:"backend"`
          Path     string `yaml:"path"`
          PathType string `yaml:"pathType"`
        } `yaml:"paths"`
      } `yaml:"http"`
    } `yaml:"rules"`
    Selector struct {
      MatchLabels struct {
        App string `yaml:"app"`
      } `yaml:"matchLabels"`
    } `yaml:"selector"`
    Template struct {
      Metadata struct {
        Labels struct {
          App string `yaml:"app"`
        } `yaml:"labels"`
      } `yaml:"metadata"`
      Spec struct {
        Containers []struct {
          EnvFrom []struct {
            ConfigMapRef struct {
              Name string `yaml:"name"`
            } `yaml:"configMapRef"`
            SecretRef struct {
              Name string `yaml:"name"`
            } `yaml:"secretRef"`
          } `yaml:"envFrom"`
          Image string `yaml:"image"`
          Name  string `yaml:"name"`
          Ports []struct {
            ContainerPort int `yaml:"containerPort"`
          } `yaml:"ports"`
        } `yaml:"containers"`
      } `yaml:"spec"`
    } `yaml:"template"`
  } `yaml:"spec"`
  Type string `yaml:"type"`
}

func ReadConfig(path string) (Config, error) {
  file, err := os.Open(path)
  if err != nil {
    return Config{}, fmt.Errorf("opening config file: %w", err)
  }
  defer file.Close()
  c := Config{}
  err = yaml.NewDecoder(file).Decode(&c)
  if err != nil {
    return Config{}, fmt.Errorf("decoding config file: %w", err)
  }
  return c, nil
}

See outputs for different flag combinations:

Install

Use version tags to avoid development versions as such:

go install github.com/ufukty/gonfique@v1.5.3

Usage

gonfique -in config.yml -out config.go -pkg main [-type-name "Config"] [-use "use.go"] [-organize] [-mappings "mappings.yml"]

Run gonfique --help for parameter details.

Features

  • Specify names for detected types via -mapping flag to export safely. More
  • Defines named or inline types depending on user choice (-organize flag).
  • Reuses user-defined types defined provided in a file when schemas match via -use flag
  • Supports arrays:
    • When all array items share same schema; array types are generated as []<item type>.
    • When all array items don't share same schema, but all items are compatible; array types are generated as []<combined item types>.
  • Supports dictionaries:
    • Implements .Range() method on those dictionaries that all values share same schema, so they can be iterable via for loops. Such as for service, details := range cfg.Services.Range() { ... }
  • Supports time.Duration values such as 200ms or 1µs.
  • Supports JSON and YAML config files.

Docs

Limitations

  • Multidocument YAML files are not supported.
  • Mapping file is for generating an exported type definition with the given name and detected type; not to specify type.
  • Type of keys with value of null gets assigned as any.

Contribution

Issues are open for discussions and rest.

License

Apache2. See LICENSE file.