Skip to content

Commit

Permalink
Validate CloudConfig using schema
Browse files Browse the repository at this point in the history
This patch ensures the CloudConfig YAML produced by the inline
CloudInit spec is validated against the official CloudConfig
schema from Cloud-Init.
  • Loading branch information
akutz committed Dec 14, 2023
1 parent 70d22b9 commit 8a065b6
Show file tree
Hide file tree
Showing 22 changed files with 7,162 additions and 14 deletions.
1 change: 1 addition & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ run:
- external/
- controllers/virtualmachineservice/v1alpha1/utils/
- controllers/virtualmachineservice/v1alpha2/utils/
- pkg/util/cloudinit/schema/

# override defaults
linters-settings:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (

"github.com/vmware-tanzu/vm-operator/api/v1alpha2/cloudinit"
"github.com/vmware-tanzu/vm-operator/api/v1alpha2/common"

"github.com/vmware-tanzu/vm-operator/pkg/util/cloudinit/validate"
)

// cloudConfig provides support for marshalling the object to a valid
Expand Down Expand Up @@ -139,6 +141,11 @@ func MarshalYAML(
return "", nil
}

// Validate the produced CloudConfig YAML using the CloudConfig schema.
if err := validate.CloudConfigYAML(data); err != nil {
return "", err
}

return data, nil
}

Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (

vmopv1cloudinit "github.com/vmware-tanzu/vm-operator/api/v1alpha2/cloudinit"
"github.com/vmware-tanzu/vm-operator/api/v1alpha2/common"
"github.com/vmware-tanzu/vm-operator/pkg/vmprovider/providers/vsphere2/cloudinit"
"github.com/vmware-tanzu/vm-operator/pkg/util/cloudinit"
)

var _ = Describe("CloudConfig GetCloudConfigSecretData", func() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (

vmopv1cloudinit "github.com/vmware-tanzu/vm-operator/api/v1alpha2/cloudinit"
"github.com/vmware-tanzu/vm-operator/api/v1alpha2/common"
"github.com/vmware-tanzu/vm-operator/pkg/vmprovider/providers/vsphere2/cloudinit"
"github.com/vmware-tanzu/vm-operator/pkg/util/cloudinit"
)

var _ = Describe("CloudConfig MarshalYAML", func() {
Expand Down
File renamed without changes.
17 changes: 17 additions & 0 deletions pkg/util/cloudinit/schema/Dockerfile.quicktype
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
FROM node:20 AS build
WORKDIR /quicktype

RUN npm install --prefix /quicktype quicktype

FROM gcr.io/distroless/nodejs20-debian12
COPY --from=build /quicktype /quicktype

WORKDIR /output
CMD [ \
"/quicktype/node_modules/quicktype/dist/index.js", \
"--src", "/schema.json", \
"--src-lang", "schema", \
"--out", "/output/cloudconfig.go", \
"--lang", "go", \
"--package", "schema" \
]
98 changes: 98 additions & 0 deletions pkg/util/cloudinit/schema/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Copyright (c) 2023 VMware, Inc. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0

# If you update this file, please follow
# https://suva.sh/posts/well-documented-makefiles

# Ensure Make is run with bash shell as some syntax below is bash-specific
SHELL := /usr/bin/env bash

.DEFAULT_GOAL := help

# Directories.
BIN_DIR := bin
NODE_PREFIX := $(abspath $(BIN_DIR))

# Binaries.
QUICKTYPE := $(NODE_PREFIX)/node_modules/.bin/quicktype

# Schemas.
SCHEMA_CLOUD_CONFIG := schema-cloud-config-v1.json

# Output.
CLOUD_CONFIG_GO := cloudconfig.go

# Images.
QUICKTYPE_IMAGE_NAME := vm-op-quicktype
QUICKTYPE_IMAGE_VERSION := latest
QUICKTYPE_IMAGE ?= $(QUICKTYPE_IMAGE_NAME):$(QUICKTYPE_IMAGE_VERSION)

# Select how to run quicktype.
ifeq (,$(shell command -v npm))
QUICKTYPE_METHOD ?= docker
endif


## --------------------------------------
## Help
## --------------------------------------

help: ## Display this help
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)

## --------------------------------------
## Image
## --------------------------------------

.PHONY: build-images-quicktype
build-images-quicktype:
docker build -t $(QUICKTYPE_IMAGE) -f Dockerfile.quicktype .

.PHONY: build-images
build-images: ## Build the docker images
$(MAKE) build-images-quicktype


## --------------------------------------
## Binaries
## --------------------------------------

quicktype: $(QUICKTYPE) ## Install quicktype
$(QUICKTYPE):
npm install --prefix $(NODE_PREFIX) quicktype


## --------------------------------------
## Generate
## --------------------------------------

$(CLOUD_CONFIG_GO): $(SCHEMA_CLOUD_CONFIG)
ifeq (docker,$(QUICKTYPE_METHOD))
$(CLOUD_CONFIG_GO): build-images-quicktype
docker run -it --rm \
-v $$(pwd):/output \
-v $$(pwd)/$(SCHEMA_CLOUD_CONFIG):/schema.json \
$(QUICKTYPE_IMAGE)
else
$(CLOUD_CONFIG_GO): | $(QUICKTYPE)
$(QUICKTYPE) \
--src $(SCHEMA_CLOUD_CONFIG) --src-lang schema \
--out $@ --lang go --package schema
endif

generate-go: ## Generate the go source code from the schemas
$(MAKE) $(CLOUD_CONFIG_GO)


## --------------------------------------
## Cleanup
## --------------------------------------

.PHONY: clean
clean: ## Run all the clean targets
rm -f cloudconfig.go

.PHONY: clobber
clobber: ## Remove all of the tooling as well
$(MAKE) clean
rm -fr $(BIN_DIR)
15 changes: 15 additions & 0 deletions pkg/util/cloudinit/schema/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Cloud-Init schemas

## Overview

This directory contains schema files relates to Cloud-Init:

* [`schema-cloud-config-v1.json`](./schema-cloud-config-v1.json)

* **`Copied`** `2023/12/14`
* **`Source`** https://github.com/canonical/cloud-init/blob/main/cloudinit/config/schemas/schema-cloud-config-v1.json
* **`--help`** The Cloud-Init CloudConfig schema that may be used to validate user and vendor data

## Generating the Go source code

Run `make generate-go`. If the local system has `npm`, it is used to install `quicktype`, which is then used to generate `cloudconfig.go` from the schema. Otherwise a container image is built from `Dockerfile.quicktype` which is then used to generate the Go source code.
Loading

0 comments on commit 8a065b6

Please sign in to comment.