Skip to content

Commit

Permalink
Update: Add cue validation, add cli flags, templates, and update README
Browse files Browse the repository at this point in the history
Signed-off-by: santoshkal <[email protected]>
  • Loading branch information
santoshkal committed Oct 31, 2023
1 parent c38ef30 commit 99ffc31
Show file tree
Hide file tree
Showing 1,518 changed files with 271,737 additions and 240 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,4 @@ lint: ## Run a linter on the codebase using golangci-lint.


build: ## builds the GenVal app for defined OS/Arch by passing GOOS=$(GOOS) GOARCH=$GOARCH args.| Example usage `make build GOOS=linux GOARCH=amd64`
@GOOS=$(GOOS) GOARCH=$(GOARCH) go build -ldflags="-X main.Version=$(shell git describe --tags --abbrev=0)" -o ./bin/genval
@GOOS=$(GOOS) GOARCH=$(GOARCH) go build -ldflags="-X main.Version=$(shell git describe --tags --abbrev=0)" -o ./bin/genval ./cmd
43 changes: 27 additions & 16 deletions main.go → cmd/container/container.go
Original file line number Diff line number Diff line change
@@ -1,45 +1,57 @@
package main
package container

import (
"fmt"
"os"

generate "github.com/intelops/genval/pkg/generate/dockerfile_gen"
"github.com/intelops/genval/pkg/parser"
"github.com/intelops/genval/pkg/utils"
validate "github.com/intelops/genval/pkg/validate/dockerfile_val"

log "github.com/sirupsen/logrus"
)

func main() {
if len(os.Args) < 3 {
log.Error("Usage: go run main.go input.json output.Dockerfile")
return
}

func init() {
// Set up logrus formatting here if needed
log.SetFormatter(&log.TextFormatter{
FullTimestamp: true,
FullTimestamp: false,
})
}

inputPath := os.Args[1]
outputPath := os.Args[2]
func Execute(value, output, inputpolicy, outputpolicy string) {
if value == "" || output == "" || inputpolicy == "" || outputpolicy == "" {
fmt.Println("[USAGE]: ./genval --mode=container --reqinput=input.json --output=output.Dockerfile --inputpolicy=<path/to/input.rego policy> --outputpolicy <path/tp/output.rego file>")
return
}

// inputPath := args[0]
// outputPath := args[1]
inputPath := value
outputPath := output
inputPolicyFile := inputpolicy
outputPolicyFile := outputpolicy

// Use ParseInputFile to read and unmarshal the input file
var data generate.DockerfileContent

err := parser.ReadAndParseFile(inputPath, &data)
input, err := utils.ProcessInputs([]string{inputPath})
if err != nil {
log.Errorf("Error reading URL: %v", err)
}

err = parser.ReadAndParseFile(input[0], &data)
if err != nil {
log.Error("Error:", err)
return
}

yamlContent, err := os.ReadFile(inputPath)
yamlContent, err := os.ReadFile(input[0])
if err != nil {
log.Fatalf("Error reading YAML file: %v", err)
}

// Validate the YAML using OPA
err = validate.ValidateInput(string(yamlContent), validate.InputPolicy)
err = validate.ValidateInput(string(yamlContent), inputPolicyFile)
if err != nil {
log.Fatalf("Validation error: %v", err)
return
Expand All @@ -55,8 +67,7 @@ func main() {
}
fmt.Printf("Generated Dockerfile saved to: %s\n", outputPath)

err = validate.ValidateDockerfile(string(outputData), validate.DockerfilePolicy)
// fmt.Printf("Dockerfile JSON: %s\n", generatedDockerfileContent)
err = validate.ValidateDockerfile(string(outputData), outputPolicyFile)
if err != nil {
log.Error("Dockerfile validation failed:", err)
return
Expand Down
104 changes: 104 additions & 0 deletions cmd/cueval/cueval.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package cueval

import (
"fmt"
"os"

"cuelang.org/go/cue"
"cuelang.org/go/cue/cuecontext"
"cuelang.org/go/cue/load"
embeder "github.com/intelops/genval"
"github.com/intelops/genval/pkg/cuecore"
"github.com/intelops/genval/pkg/parser"
"github.com/intelops/genval/pkg/utils"
log "github.com/sirupsen/logrus"
)

func Execute(resource, reqinput string, policies ...string) {

const modPath = "github.com/intelops/genval"
staticFS := embeder.CueDef

td, cleanup, err := utils.TempDirWithCleanup()
if err != nil {
log.Fatal(err)
}
defer cleanup()

ctx := cuecontext.New()

if resource == "" || reqinput == "" || len(policies) == 0 {
fmt.Println("[Usage]: genval --mode=cue --resource=<Resource> --reqinput=<Input JSON> --policy <path/to/.cue schema file>")
return
}

defPath := resource
dataFile := reqinput
schemaFile := policies

definitions, err := utils.ProcessInputs(schemaFile)
if err != nil {
log.Errorf("Error reading URL: %v", err)
}
overlay, err := utils.GenerateOverlay(staticFS, td, definitions)
if err != nil {
log.Fatal(err)
}

conf := &load.Config{
Dir: td,
Overlay: overlay,
Module: modPath,
Package: "*",
}

input, err := utils.ProcessInputs([]string{dataFile})
if err != nil {
log.Errorf("Error reading URL: %v", err)
}
res, data, err := utils.ReadAndCompileData(defPath, input[0])
if err != nil {
log.Fatalf("Error processing data: %v", err)
return
}

defName := "#" + defPath

v, err := cuecore.BuildInstance(ctx, definitions, conf)
if err != nil {
log.Fatal(err)
}

for _, value := range v {
lookUp := cue.ParsePath(defName)
def := value.LookupPath(lookUp)
if def.Err() != nil {
log.Errorf("Error parsing Path: %v", def.Err())
return
}

unifiedValue, err := cuecore.UnifyAndValidate(def, data)
if err != nil {
log.Errorf("Validation failed: %v", err)
return
}

yamlData, err := parser.CueToYAML(unifiedValue)
if err != nil {
log.Errorf("Error Marshaling: %v", err)
return
}

err = os.WriteFile(res+".yaml", yamlData, 0644)
if err != nil {
log.Errorf("Writing YAML: %v", err)
return
}
}

if err := utils.CleanupDownloadedDir(); err != nil {
log.Errorf("Error removing cue_downloads directory: %v", err)
}

log.Infof("validation for %v succeeded, generated manifest: %v.yaml\n\n", defPath, res)
}
81 changes: 81 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package main

import (
"flag"
"fmt"
"os"
"strings"

"github.com/intelops/genval/cmd/container"
"github.com/intelops/genval/cmd/cueval"
)

var mode, resource, reqinput, output, inputpolicy, outputpolicy string

var policies multiValueFlag

func init() {
flag.StringVar(&mode, "mode", "", "Specify mode: 'container' for Dockerfile validation/generation or 'cue' for K8s resource validation/generation.")
flag.StringVar(&resource, "resource", "", "Resource for K8s mode (cueval).")
flag.StringVar(&reqinput, "reqinput", "", "Input value (JSON) for K8s mode (cueval).")
flag.StringVar(&output, "output", "", "Output path for Dockerfile for in container mode.")
flag.Var(&policies, "policy", "Validation policies, .cue files to be used in cue mode.")
flag.StringVar(&inputpolicy, "inputpolicy", "", "Rego policy to validate JSON input in container mode.")
flag.StringVar(&outputpolicy, "outputpolicy", "", "Rego policy to validate generated Dockerfile in container mode.")

flag.Usage = func() {
fmt.Fprintf(os.Stderr, `Usage of genval:
Modes:
container: Dockerfile validation and generation. Arguments: <reqinput.json> <output.Dockerfile> <input.rego policy file> <output.rego policy file>
Example usage:
./genval --mode=container --reqinput=input.json \
--output=output.Dockerfile \
--inputpolicy=<path/to/input.rego policy> \
--outputpolicy <path/tp/output.rego file>
cue: K8s resource validation and generation. Arguments: <reqinput.json> <resource> <CUE schema policy> .
Example usage:
./genval --mode=cue --resource=Deployment \
--reqinput=deployment.json \
--policy=<path/to/.cue schema>
The "resource" arg in "cue" mode needs a valid Kind, like in above example "Deployment" or StatefulSet, DaemonSet etc.
`)

flag.PrintDefaults()
}
}

func main() {
flag.Parse()

// Pass arguments after the mode flag

switch mode {
case "container":
// Call the Docker mode's execution function
container.Execute(reqinput, output, inputpolicy, outputpolicy)
case "cue":
// Call the K8s mode's execution function
cueval.Execute(resource, reqinput, policies...)
default:
fmt.Println("Invalid mode. Choose 'container' or 'cue'.")
flag.Usage()
}
}

// Implement flag.Value for a slice of strings
type multiValueFlag []string

func (m *multiValueFlag) String() string {
return strings.Join(*m, ", ")
}

func (m *multiValueFlag) Set(value string) error {
*m = append(*m, value)
return nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Code generated by cue get go. DO NOT EDIT.

//cue:generate cue get go github.com/argoproj/argo-cd/v2/pkg/apis/application

package application

// API Group
#Group: "argoproj.io"

// Application constants
#ApplicationKind: "Application"
#ApplicationSingular: "application"
#ApplicationPlural: "applications"
#ApplicationShortName: "app"
#ApplicationFullName: "applications.argoproj.io"

// AppProject constants
#AppProjectKind: "AppProject"
#AppProjectSingular: "appproject"
#AppProjectPlural: "appprojects"
#AppProjectShortName: "appproject"
#AppProjectFullName: "appprojects.argoproj.io"

// ApplicationSet constants
#ApplicationSetKind: "ApplicationSet"
#ApplicationSetSingular: "applicationset"
#ApplicationSetShortName: "appset"
#ApplicationSetPlural: "applicationsets"
#ApplicationSetFullName: "applicationsets.argoproj.io"
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Code generated by cue get go. DO NOT EDIT.

//cue:generate cue get go github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1

package v1alpha1

import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

// AppProjectList is list of AppProject resources
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
#AppProjectList: {
metav1.#TypeMeta
metadata: metav1.#ListMeta @go(ListMeta) @protobuf(1,bytes,opt)
items: [...#AppProject] @go(Items,[]AppProject) @protobuf(2,bytes,rep)
}

// AppProject provides a logical grouping of applications, providing controls for:
// * where the apps may deploy to (cluster whitelist)
// * what may be deployed (repository whitelist, resource whitelist/blacklist)
// * who can access these applications (roles, OIDC group claims bindings)
// * and what they can do (RBAC policies)
// * automation access to these roles (JWT tokens)
// +genclient
// +genclient:noStatus
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +kubebuilder:resource:path=appprojects,shortName=appproj;appprojs
#AppProject: {
metav1.#TypeMeta
metadata: metav1.#ObjectMeta @go(ObjectMeta) @protobuf(1,bytes,opt)
spec: #AppProjectSpec @go(Spec) @protobuf(2,bytes,opt)
status?: #AppProjectStatus @go(Status) @protobuf(3,bytes,opt)
}

// AppProjectStatus contains status information for AppProject CRs
#AppProjectStatus: {
// JWTTokensByRole contains a list of JWT tokens issued for a given role
jwtTokensByRole?: {[string]: #JWTTokens} @go(JWTTokensByRole,map[string]JWTTokens) @protobuf(1,bytes,opt)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Code generated by cue get go. DO NOT EDIT.

//cue:generate cue get go github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1

package v1alpha1

// AnnotationKeyRefresh is the annotation key which indicates that app needs to be refreshed. Removed by application controller after app is refreshed.
// Might take values 'normal'/'hard'. Value 'hard' means manifest cache and target cluster state cache should be invalidated before refresh.
#AnnotationKeyRefresh: "argocd.argoproj.io/refresh"

// AnnotationKeyManifestGeneratePaths is an annotation that contains a list of semicolon-separated paths in the
// manifests repository that affects the manifest generation. Paths might be either relative or absolute. The
// absolute path means an absolute path within the repository and the relative path is relative to the application
// source path within the repository.
#AnnotationKeyManifestGeneratePaths: "argocd.argoproj.io/manifest-generate-paths"
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Code generated by cue get go. DO NOT EDIT.

//cue:generate cue get go github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1

package v1alpha1

import "time"

#DefaultSyncRetryMaxDuration: time.#Duration & 180000000000
#DefaultSyncRetryDuration: time.#Duration & 5000000000
#DefaultSyncRetryFactor: int64 & 2

// ResourcesFinalizerName is the finalizer value which we inject to finalize deletion of an application
#ResourcesFinalizerName: "resources-finalizer.argocd.argoproj.io"

// ForegroundPropagationPolicyFinalizer is the finalizer we inject to delete application with foreground propagation policy
#ForegroundPropagationPolicyFinalizer: "resources-finalizer.argocd.argoproj.io/foreground"

// BackgroundPropagationPolicyFinalizer is the finalizer we inject to delete application with background propagation policy
#BackgroundPropagationPolicyFinalizer: "resources-finalizer.argocd.argoproj.io/background"

// DefaultAppProjectName contains name of 'default' app project, which is available in every Argo CD installation
#DefaultAppProjectName: "default"

// RevisionHistoryLimit is the max number of successful sync to keep in history
#RevisionHistoryLimit: 10

// KubernetesInternalAPIServerAddr is address of the k8s API server when accessing internal to the cluster
#KubernetesInternalAPIServerAddr: "https://kubernetes.default.svc"
Loading

0 comments on commit 99ffc31

Please sign in to comment.