packages/image-env-to-json/cmd/root.go (148 lines of code) (raw):
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package cmd
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"github.com/santhosh-tekuri/jsonschema/v6"
"github.com/spf13/cobra"
)
type CmdConfig struct {
Version string
}
const (
EnvJsonFile = "env.json"
)
func RootCmd(cfg CmdConfig) *cobra.Command {
var (
envJsonDirectory string
jsonSchemaPath string
)
var cmd = &cobra.Command{
Use: "image-env-to-json",
Short: "A CLI tool to create a 'env.json' file from a JSON Schema.",
Long: `
This tool uses the "json-schema" JSON Schema file and creates a 'env.json' environment file in the specified "directory". Paths can be relative or absolute.
`,
Example: `
# Run the
image-env-to-json --directory /path/to/env.json --json-schema /path/to/jsonSchema.json
`,
Run: func(cmd *cobra.Command, args []string) {
if err := generateEnvJson(envJsonDirectory, jsonSchemaPath); err != nil {
fmt.Printf("[image-env-to-json] Error: '%v'.", err)
os.Exit(1)
}
},
}
cmd.Version = cfg.Version
cmd.SetVersionTemplate(`{{printf "%s\n" .Version}}`)
cmd.PersistentFlags().StringVarP(&envJsonDirectory, "directory", "d", "", "directory to create or update an existing env.json file.")
cmd.PersistentFlags().StringVar(&jsonSchemaPath, "json-schema", "", "JSON Schema file path to validate.")
cmd.MarkFlagRequired("directory")
cmd.MarkFlagRequired("json-schema")
return cmd
}
func generateEnvJson(envJsonDirectory string, jsonSchemaPath string) error {
// Check if directory exists
if _, err := os.Stat(envJsonDirectory); err != nil {
if os.IsNotExist(err) {
fmt.Printf("[image-env-to-json] Directory '%s' does not exist. Please provide an existing directory.\n", envJsonDirectory)
return err
} else {
fmt.Printf("[image-env-to-json] Error while reading the '%s'.\n", envJsonDirectory)
return err
}
}
// Open the JSON Schema file in the `jsonSchemaPath`
fmt.Printf("[image-env-to-json] Reading JSON Schema from '%s'...\n", jsonSchemaPath)
jsonSchemaFile, err := os.Open(jsonSchemaPath)
if err != nil {
fmt.Printf("[image-env-to-json] Error reading file '%s'.\n", jsonSchemaPath)
return err
}
defer jsonSchemaFile.Close()
// Unmarshal the `jsonSchemaFile`
jsonSchema, err := jsonschema.UnmarshalJSON(jsonSchemaFile)
if err != nil {
fmt.Printf("[image-env-to-json] Failed to unmarshal JSON Schema file: %s.\n", jsonSchemaPath)
return err
}
// Walk though the `jsonSchema` properties, saving the environment variable names
id := fmt.Sprintf("%v", jsonSchema.(map[string]any)["$id"])
properties, _ := jsonSchema.(map[string]any)["definitions"].(map[string]any)[id].(map[string]any)["properties"].(map[string]any)
envVarNames := make([]string, 0, len(properties))
for key := range properties {
envVarNames = append(envVarNames, key)
}
fmt.Printf("[image-env-to-json] Extracted environment variables '%s' \n", envVarNames)
var envJsonFile []byte
var envJsonPath = filepath.Join(envJsonDirectory, EnvJsonFile)
fmt.Printf("[image-env-to-json] Checking if '%s' file exists.\n", envJsonPath)
if _, err := os.Stat(envJsonPath); err != nil {
if os.IsNotExist(err) {
fmt.Printf("[image-env-to-json] Couldn't find '%s' file. Using an empty object as starting point.\n", envJsonPath)
envJsonFile = []byte("{}")
} else {
fmt.Printf("[image-env-to-json] Error while reading the '%s' file.\n", envJsonPath)
return err
}
} else {
envJsonFile, err = os.ReadFile(envJsonPath)
if err != nil {
fmt.Printf("[image-env-to-json] Error reading %s\n", EnvJsonFile)
return err
}
}
// Unmarshal the envJsonFile
var envJson map[string]any
if err := json.Unmarshal(envJsonFile, &envJson); err != nil {
fmt.Printf("[image-env-to-json] Error parsing %s\n", EnvJsonFile)
return err
}
// Update the `env.json` file with the environment variables
isUpdated := false
for _, name := range envVarNames {
envVarStringValue, exists := os.LookupEnv(name)
if !exists {
fmt.Printf("[image-env-to-json] '%s' environment variable isn't set.\n", name)
} else {
var jsonValue any
if err := json.Unmarshal([]byte(envVarStringValue), &jsonValue); err != nil {
fmt.Printf("[image-env-to-json] Couldn't infer '%s' value. Using '%s' as string.\n", name, name)
isUpdated = true
envJson[name] = envVarStringValue
} else {
fmt.Printf("[image-env-to-json] '%s' is a valid JSON. Using parsed JSON from '%s'.\n", name, name)
envJson[name] = jsonValue
isUpdated = true
}
}
}
if isUpdated {
fmt.Printf("[image-env-to-json] Environment variables were updated.\n")
} else {
fmt.Printf("[image-env-to-json] Environment variables weren't updated.\n")
}
// Marshal envJson content
updatedEnvJson, err := json.MarshalIndent(envJson, "", " ")
if err != nil {
fmt.Printf("[image-env-to-json] Error encoding %s\n", EnvJsonFile)
return err
}
fmt.Printf("[image-env-to-json] Validating new '%s' before saving it.\n", EnvJsonFile)
if jsonSchema != nil {
compiler := jsonschema.NewCompiler()
compiledSchema, err := compiler.Compile(jsonSchemaPath)
if err != nil {
fmt.Printf("[image-env-to-json] Error compiling '%s' file.\n", jsonSchemaPath)
return err
}
err = compiledSchema.Validate(envJson)
if err != nil {
fmt.Printf("[image-env-to-json] Error validating '%s' file.\n", EnvJsonFile)
return err
}
fmt.Printf("[image-env-to-json] '%s' is valid.\n", EnvJsonFile)
}
fmt.Printf("[image-env-to-json] Writing '%s' at '%s'.\n", EnvJsonFile, envJsonDirectory)
if err := os.WriteFile(envJsonPath, updatedEnvJson, 0644); err != nil {
fmt.Printf("[image-env-to-json] Error writing '%s'.\n", EnvJsonFile)
return err
}
fmt.Printf("[image-env-to-json] Done.\n")
return nil
}