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  }