github.com/zntrio/harp/v2@v2.0.9/pkg/tasks/template/fs.go (about) 1 // Licensed to Elasticsearch B.V. under one or more contributor 2 // license agreements. See the NOTICE file distributed with 3 // this work for additional information regarding copyright 4 // ownership. Elasticsearch B.V. licenses this file to you under 5 // the Apache License, Version 2.0 (the "License"); you may 6 // not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, 12 // software distributed under the License is distributed on an 13 // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 // KIND, either express or implied. See the License for the 15 // specific language governing permissions and limitations 16 // under the License. 17 18 package template 19 20 import ( 21 "context" 22 "fmt" 23 "io/fs" 24 25 "github.com/psanford/memfs" 26 27 "github.com/zntrio/harp/v2/pkg/sdk/fsutil" 28 "github.com/zntrio/harp/v2/pkg/template/engine" 29 ) 30 31 // FileSystemTask implements filesystem template rendering task. 32 type FileSystemTask struct { 33 InputPath string 34 OutputPath string 35 ValueFiles []string 36 SecretLoaders []string 37 Values []string 38 StringValues []string 39 FileValues []string 40 LeftDelims string 41 RightDelims string 42 AltDelims bool 43 FileLoaderRootPath string 44 DryRun bool 45 } 46 47 // Run the task. 48 func (t *FileSystemTask) Run(ctx context.Context) error { 49 // Prepare input filesystem 50 inFS, err := fsutil.From(t.InputPath) 51 if err != nil { 52 return fmt.Errorf("unable to prepare input filesystem: %w", err) 53 } 54 55 // Prepare embedded files 56 var ( 57 fileRootFS fs.FS 58 ) 59 if t.FileLoaderRootPath != "" { 60 var errRootFS error 61 fileRootFS, errRootFS = fsutil.From(t.FileLoaderRootPath) 62 if errRootFS != nil { 63 return fmt.Errorf("unable load files filesystem: %w", errRootFS) 64 } 65 } 66 67 // Prepare render context 68 renderCtx, err := prepareRenderContext(ctx, &renderContextConfig{ 69 ValueFiles: t.ValueFiles, 70 SecretLoaders: t.SecretLoaders, 71 Values: t.Values, 72 StringValues: t.StringValues, 73 FileValues: t.FileValues, 74 LeftDelims: t.LeftDelims, 75 RightDelims: t.RightDelims, 76 AltDelims: t.AltDelims, 77 FileRootPath: fileRootFS, 78 }) 79 if err != nil { 80 return fmt.Errorf("unable to prepare rendering context: %w", err) 81 } 82 83 // Memory filesystem 84 outFs := memfs.New() 85 86 // Generate all files from input filesystem. 87 if err := fs.WalkDir(inFS, ".", func(path string, d fs.DirEntry, errWalk error) error { 88 if errWalk != nil { 89 return errWalk 90 } 91 if d.IsDir() { 92 return outFs.MkdirAll(path, 0o750) 93 } 94 95 // Get file content. 96 body, err := fs.ReadFile(inFS, path) 97 if err != nil { 98 return fmt.Errorf("unable to retrieve file content %q: %w", path, err) 99 } 100 101 // Compile and execute template 102 out, err := engine.RenderContext(renderCtx, string(body)) 103 if err != nil { 104 return fmt.Errorf("unable to produce output content for file %q: %w", path, err) 105 } 106 107 // Create output file. 108 return outFs.WriteFile(path, []byte(out), 0o444) 109 }); err != nil { 110 return fmt.Errorf("unable to render filesytem: %w", err) 111 } 112 113 // Skip copy if no output is defined. 114 if t.DryRun || t.OutputPath == "" { 115 return nil 116 } 117 118 // Dump filesystem 119 return fsutil.Dump(outFs, t.OutputPath) 120 }