Skip to content

Commit

Permalink
fix: support v1beta2 for CRD and fix indentation and a few minor chan…
Browse files Browse the repository at this point in the history
…ges (#118)

* fix: support v1beta2 for CRD and fix indentation and a few minor changes

* extended the test suite
  • Loading branch information
Skarlso authored Oct 11, 2024
1 parent 2ffd548 commit 8d2a07b
Show file tree
Hide file tree
Showing 30 changed files with 12,305 additions and 156 deletions.
39 changes: 38 additions & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ permissions:
contents: read # for actions/checkout to fetch code

jobs:
run-test-suite:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout
Expand All @@ -34,5 +34,42 @@ jobs:
${{ runner.os }}-go-
- name: Run lint
run: make lint

test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: '${{ github.workspace }}/go.mod'
- name: Restore Go cache
uses: actions/cache@v4
with:
path: /home/runner/work/_temp/_github_home/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Run tests
run: make test

crd-test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: '${{ github.workspace }}/go.mod'
- name: Restore Go cache
uses: actions/cache@v4
with:
path: /home/runner/work/_temp/_github_home/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Run CRD Test
run: |
make build && ./cty test sample-tests
8 changes: 4 additions & 4 deletions cmd/file_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"fmt"
"os"

v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
"k8s.io/apimachinery/pkg/util/yaml"

"github.com/Skarlso/crd-to-sample-yaml/pkg/sanitize"
Expand All @@ -14,7 +14,7 @@ type FileHandler struct {
location string
}

func (h *FileHandler) CRDs() ([]*v1.CustomResourceDefinition, error) {
func (h *FileHandler) CRDs() ([]*v1beta1.CustomResourceDefinition, error) {
if _, err := os.Stat(h.location); os.IsNotExist(err) {
return nil, fmt.Errorf("file under '%s' does not exist", h.location)
}
Expand All @@ -28,10 +28,10 @@ func (h *FileHandler) CRDs() ([]*v1.CustomResourceDefinition, error) {
return nil, fmt.Errorf("failed to sanitize content: %w", err)
}

crd := &v1.CustomResourceDefinition{}
crd := &v1beta1.CustomResourceDefinition{}
if err := yaml.Unmarshal(content, crd); err != nil {
return nil, fmt.Errorf("failed to unmarshal into custom resource definition: %w", err)
}

return []*v1.CustomResourceDefinition{crd}, nil
return []*v1beta1.CustomResourceDefinition{crd}, nil
}
8 changes: 4 additions & 4 deletions cmd/folder_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"os"
"path/filepath"

v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
"k8s.io/apimachinery/pkg/util/yaml"

"github.com/Skarlso/crd-to-sample-yaml/pkg/sanitize"
Expand All @@ -16,12 +16,12 @@ type FolderHandler struct {
location string
}

func (h *FolderHandler) CRDs() ([]*v1.CustomResourceDefinition, error) {
func (h *FolderHandler) CRDs() ([]*v1beta1.CustomResourceDefinition, error) {
if _, err := os.Stat(h.location); os.IsNotExist(err) {
return nil, fmt.Errorf("file under '%s' does not exist", h.location)
}

var crds []*v1.CustomResourceDefinition
var crds []*v1beta1.CustomResourceDefinition

if err := filepath.Walk(h.location, func(path string, info fs.FileInfo, err error) error {
if err != nil {
Expand All @@ -48,7 +48,7 @@ func (h *FolderHandler) CRDs() ([]*v1.CustomResourceDefinition, error) {
return fmt.Errorf("failed to sanitize content: %w", err)
}

crd := &v1.CustomResourceDefinition{}
crd := &v1beta1.CustomResourceDefinition{}
if err := yaml.Unmarshal(content, crd); err != nil {
fmt.Fprintln(os.Stderr, "skipping none CRD file: "+path)

Expand Down
4 changes: 2 additions & 2 deletions cmd/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"path/filepath"

"github.com/spf13/cobra"
v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"

"github.com/Skarlso/crd-to-sample-yaml/pkg"
)
Expand Down Expand Up @@ -45,7 +45,7 @@ var (
)

type Handler interface {
CRDs() ([]*v1.CustomResourceDefinition, error)
CRDs() ([]*v1beta1.CustomResourceDefinition, error)
}

func init() {
Expand Down
8 changes: 4 additions & 4 deletions cmd/url_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"net/http"
"time"

v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
"k8s.io/apimachinery/pkg/util/yaml"

"github.com/Skarlso/crd-to-sample-yaml/pkg/fetcher"
Expand All @@ -21,7 +21,7 @@ type URLHandler struct {
token string
}

func (h *URLHandler) CRDs() ([]*v1.CustomResourceDefinition, error) {
func (h *URLHandler) CRDs() ([]*v1beta1.CustomResourceDefinition, error) {
client := http.DefaultClient
client.Timeout = timeout * time.Second

Expand All @@ -36,10 +36,10 @@ func (h *URLHandler) CRDs() ([]*v1.CustomResourceDefinition, error) {
return nil, fmt.Errorf("failed to sanitize content: %w", err)
}

crd := &v1.CustomResourceDefinition{}
crd := &v1beta1.CustomResourceDefinition{}
if err := yaml.Unmarshal(content, crd); err != nil {
return nil, fmt.Errorf("failed to unmarshal into custom resource definition: %w", err)
}

return []*v1.CustomResourceDefinition{crd}, nil
return []*v1beta1.CustomResourceDefinition{crd}, nil
}
56 changes: 38 additions & 18 deletions pkg/create_html_output.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"slices"
"sort"

v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
)

type Index struct {
Expand Down Expand Up @@ -66,7 +66,7 @@ func LoadTemplates() error {
}

// RenderContent creates an HTML website from the CRD content.
func RenderContent(w io.WriteCloser, crds []*v1.CustomResourceDefinition, comments, minimal bool) (err error) {
func RenderContent(w io.WriteCloser, crds []*v1beta1.CustomResourceDefinition, comments, minimal bool) (err error) {
defer func() {
if cerr := w.Close(); cerr != nil {
err = errors.Join(err, cerr)
Expand All @@ -80,26 +80,25 @@ func RenderContent(w io.WriteCloser, crds []*v1.CustomResourceDefinition, commen
parser := NewParser(crd.Spec.Group, crd.Spec.Names.Kind, comments, minimal, false)

for _, version := range crd.Spec.Versions {
out, err := parseCRD(version.Schema.OpenAPIV3Schema.Properties, version.Name, minimal, RootRequiredFields)
v, err := generate(crd, version.Schema.OpenAPIV3Schema, minimal, parser)
if err != nil {
return fmt.Errorf("failed to parse properties: %w", err)
}
var buffer []byte
buf := bytes.NewBuffer(buffer)
if err := parser.ParseProperties(version.Name, buf, version.Schema.OpenAPIV3Schema.Properties); err != nil {
return fmt.Errorf("failed to generate yaml sample: %w", err)
}
versions = append(versions, Version{
Version: version.Name,
Properties: out,
Kind: crd.Spec.Names.Kind,
Group: crd.Spec.Group,
Description: version.Schema.OpenAPIV3Schema.Description,
YAML: buf.String(),
})

versions = append(versions, v)
}

if len(versions) == 0 {
// parse validation instead
if len(versions) == 0 && crd.Spec.Validation != nil {
crd.Spec.Validation.OpenAPIV3Schema.Properties["kind"] = v1beta1.JSONSchemaProps{}
crd.Spec.Validation.OpenAPIV3Schema.Properties["apiVersion"] = v1beta1.JSONSchemaProps{}
version, err := generate(crd, crd.Spec.Validation.OpenAPIV3Schema, minimal, parser)
if err != nil {
return fmt.Errorf("failed to generate yaml sample: %w", err)
}

versions = append(versions, version)
} else if len(versions) == 0 {
continue
}

Expand All @@ -124,6 +123,27 @@ func RenderContent(w io.WriteCloser, crds []*v1.CustomResourceDefinition, commen
return nil
}

func generate(crd *v1beta1.CustomResourceDefinition, properties *v1beta1.JSONSchemaProps, minimal bool, parser *Parser) (Version, error) {
out, err := parseCRD(properties.Properties, crd.Name, minimal, RootRequiredFields)
if err != nil {
return Version{}, fmt.Errorf("failed to parse properties: %w", err)
}
var buffer []byte
buf := bytes.NewBuffer(buffer)
if err := parser.ParseProperties(crd.Name, buf, properties.Properties); err != nil {
return Version{}, fmt.Errorf("failed to generate yaml sample: %w", err)
}

return Version{
Version: crd.Name,
Properties: out,
Kind: crd.Spec.Names.Kind,
Group: crd.Spec.Group,
Description: properties.Description,
YAML: buf.String(),
}, nil
}

// Property builds up a Tree structure of embedded things.
type Property struct {
Name string
Expand All @@ -141,7 +161,7 @@ type Property struct {

// parseCRD takes the properties and constructs a linked list out of the embedded properties that the recursive
// template can call and construct linked divs.
func parseCRD(properties map[string]v1.JSONSchemaProps, version string, minimal bool, requiredList []string) ([]*Property, error) {
func parseCRD(properties map[string]v1beta1.JSONSchemaProps, version string, minimal bool, requiredList []string) ([]*Property, error) {
output := make([]*Property, 0, len(properties))
sortedKeys := make([]string, 0, len(properties))

Expand Down
24 changes: 19 additions & 5 deletions pkg/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ import (
"strings"

"github.com/brianvoe/gofakeit/v6"
v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
)

const array = "array"

var RootRequiredFields = []string{"apiVersion", "kind", "spec", "metadata"}

// Generate takes a CRD content and path, and outputs.
func Generate(crd *v1.CustomResourceDefinition, w io.WriteCloser, enableComments, minimal, skipRandom bool) (err error) {
func Generate(crd *v1beta1.CustomResourceDefinition, w io.WriteCloser, enableComments, minimal, skipRandom bool) (err error) {
defer func() {
if cerr := w.Close(); cerr != nil {
err = errors.Join(err, cerr)
Expand All @@ -39,6 +39,15 @@ func Generate(crd *v1.CustomResourceDefinition, w io.WriteCloser, enableComments
}
}

// Parse validation instead
if len(crd.Spec.Versions) == 0 && crd.Spec.Validation != nil {
crd.Spec.Validation.OpenAPIV3Schema.Properties["kind"] = v1beta1.JSONSchemaProps{}
crd.Spec.Validation.OpenAPIV3Schema.Properties["apiVersion"] = v1beta1.JSONSchemaProps{}
if err := parser.ParseProperties(crd.Name, w, crd.Spec.Validation.OpenAPIV3Schema.Properties); err != nil {
return fmt.Errorf("failed to parse properties: %w", err)
}
}

return nil
}

Expand Down Expand Up @@ -77,7 +86,7 @@ func NewParser(group, kind string, comments, requiredOnly, skipRandom bool) *Par
// ParseProperties takes a writer and puts out any information / properties it encounters during the runs.
// It will recursively parse every "properties:" and "additionalProperties:". Using the types, it will also output
// some sample data based on those types.
func (p *Parser) ParseProperties(version string, file io.Writer, properties map[string]v1.JSONSchemaProps) error {
func (p *Parser) ParseProperties(version string, file io.Writer, properties map[string]v1beta1.JSONSchemaProps) error {
sortedKeys := make([]string, 0, len(properties))
for k := range properties {
sortedKeys = append(sortedKeys, k)
Expand Down Expand Up @@ -189,7 +198,8 @@ func (p *Parser) ParseProperties(version string, file io.Writer, properties map[
return nil
}

func (p *Parser) emptyAfterTrimRequired(properties map[string]v1.JSONSchemaProps, required []string) bool {
// deletes properties from the properties that aren't required.
func (p *Parser) emptyAfterTrimRequired(properties map[string]v1beta1.JSONSchemaProps, required []string) bool {
for k := range properties {
if !slices.Contains(required, k) {
delete(properties, k)
Expand All @@ -200,7 +210,7 @@ func (p *Parser) emptyAfterTrimRequired(properties map[string]v1.JSONSchemaProps
}

// outputValueType generate an output value based on the given type.
func outputValueType(v v1.JSONSchemaProps, skipRandom bool) string {
func outputValueType(v v1beta1.JSONSchemaProps, skipRandom bool) string {
if v.Default != nil {
return string(v.Default.Raw)
}
Expand All @@ -224,6 +234,10 @@ func outputValueType(v v1.JSONSchemaProps, skipRandom bool) string {
st := "string"
switch v.Type {
case st:
if v.Format == "date-time" {
return "2024-10-11T12:48:44Z"
}

return st
case "integer":
if v.Minimum != nil {
Expand Down
Loading

0 comments on commit 8d2a07b

Please sign in to comment.