github.com/helmwave/helmwave@v0.36.4-0.20240509190856-b35563eba4c6/pkg/plan/new.go (about) 1 package plan 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "os" 8 "path/filepath" 9 10 "github.com/helmwave/helmwave/pkg/helper" 11 "github.com/helmwave/helmwave/pkg/hooks" 12 "github.com/helmwave/helmwave/pkg/monitor" 13 "github.com/helmwave/helmwave/pkg/registry" 14 "github.com/helmwave/helmwave/pkg/release" 15 "github.com/helmwave/helmwave/pkg/release/uniqname" 16 "github.com/helmwave/helmwave/pkg/repo" 17 "github.com/helmwave/helmwave/pkg/version" 18 "github.com/invopop/jsonschema" 19 log "github.com/sirupsen/logrus" 20 "gopkg.in/yaml.v3" 21 ) 22 23 const ( 24 // Dir is the default directory for generated files. 25 Dir = ".helmwave/" 26 27 // File is the default file name for planfile. 28 File = "planfile" 29 30 // Body is a default file name for the main config. 31 Body = "helmwave.yml" 32 33 // Manifest is the default directory under Dir for manifests. 34 Manifest = "manifest/" 35 36 // Values is default directory for values. 37 Values = "values/" 38 ) 39 40 // Plan contains full helmwave state. 41 type Plan struct { 42 body *planBody 43 dir string 44 fullPath string 45 tmpDir string 46 graphMD string 47 templater string 48 49 manifests map[uniqname.UniqName]string 50 unchanged release.Configs 51 } 52 53 // NewAndImport wrapper for New and Import in one. 54 func NewAndImport(ctx context.Context, src string) (p *Plan, err error) { 55 p = New(src) 56 57 err = p.Import(ctx) 58 if err != nil { 59 return p, err 60 } 61 62 return p, nil 63 } 64 65 // Logger will pretty build log.Entry. 66 func (p *Plan) Logger() *log.Entry { 67 a := helper.SlicesMap(p.body.Releases, func(r release.Config) string { 68 return r.Uniq().String() 69 }) 70 71 b := helper.SlicesMap(p.body.Repositories, func(r repo.Config) string { 72 return r.Name() 73 }) 74 75 c := helper.SlicesMap(p.body.Registries, func(r registry.Config) string { 76 return r.Host() 77 }) 78 79 return log.WithFields(log.Fields{ 80 "releases": a, 81 "repositories": b, 82 "registries": c, 83 }) 84 } 85 86 //nolint:lll 87 type planBody struct { 88 Project string `yaml:"project" json:"project" jsonschema:"title=project name,description=reserved for future,example=my-awesome-project"` 89 Version string `yaml:"version" json:"version" jsonschema:"title=version of helmwave,description=will check current version and project version,pattern=^[0-9]+\\.[0-9]+\\.[0-9]+$,example=0.23.0,example=0.22.1"` 90 Monitors monitor.Configs `yaml:"monitors" json:"monitors" jsonschema:"title=monitors list"` 91 Repositories repo.Configs `yaml:"repositories" json:"repositories" jsonschema:"title=repositories list,description=helm repositories"` 92 Registries registry.Configs `yaml:"registries" json:"registries" jsonschema:"title=registries list,description=helm OCI registries"` 93 Releases release.Configs `yaml:"releases" json:"releases" jsonschema:"title=helm releases,description=what you wanna deploy"` 94 Lifecycle hooks.Lifecycle `yaml:"lifecycle" json:"lifecycle" jsonschema:"title=lifecycle,description=helmwave lifecycle hooks"` 95 } 96 97 func GenSchema() *jsonschema.Schema { 98 r := &jsonschema.Reflector{ 99 DoNotReference: true, 100 RequiredFromJSONSchemaTags: true, 101 } 102 103 schema := r.Reflect(&planBody{}) 104 schema.AdditionalProperties = jsonschema.TrueSchema // to allow anchors at the top level 105 106 return schema 107 } 108 109 // NewBody parses plan from file. 110 func NewBody(_ context.Context, file string, validate bool) (*planBody, error) { 111 b := &planBody{ 112 Version: version.Version, 113 } 114 115 src, err := os.ReadFile(file) 116 if err != nil { 117 return b, fmt.Errorf("failed to read plan file %s: %w", file, err) 118 } 119 120 decoder := yaml.NewDecoder(bytes.NewBuffer(src)) 121 err = decoder.Decode(b) 122 if err != nil { 123 return b, fmt.Errorf("failed to unmarshal YAML plan %s: %w", file, err) 124 } 125 126 if validate { 127 err = b.Validate() 128 if err != nil { 129 return nil, err 130 } 131 } 132 133 return b, nil 134 } 135 136 // New returns empty *Plan for provided directory. 137 func New(dir string) *Plan { 138 tmpDir, err := os.MkdirTemp("", "") 139 if err != nil { 140 log.WithError(err).Warn("failed to create temporary directory") 141 tmpDir = os.TempDir() 142 } 143 144 plan := &Plan{ 145 tmpDir: tmpDir, 146 dir: dir, 147 fullPath: filepath.Join(dir, File), 148 manifests: make(map[uniqname.UniqName]string), 149 } 150 151 return plan 152 }