github.com/sealerio/sealer@v0.11.1-0.20240507115618-f4f89c5853ae/pkg/application/files.go (about)

     1  // Copyright © 2023 Alibaba Group Holding Ltd.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package application
    16  
    17  import (
    18  	"bytes"
    19  	"errors"
    20  	"fmt"
    21  	"io"
    22  	"os"
    23  	"path/filepath"
    24  
    25  	"github.com/imdario/mergo"
    26  	"github.com/sealerio/sealer/pkg/env"
    27  	v2 "github.com/sealerio/sealer/types/api/v2"
    28  	osUtils "github.com/sealerio/sealer/utils/os"
    29  	"github.com/sirupsen/logrus"
    30  	"gopkg.in/yaml.v3"
    31  )
    32  
    33  func newFileProcessor(appFile v2.AppFile) (FileProcessor, error) {
    34  	switch appFile.Strategy {
    35  	case v2.OverWriteStrategy:
    36  		return overWriteProcessor{appFile}, nil
    37  	case v2.MergeStrategy:
    38  		return mergeProcessor{appFile}, nil
    39  	}
    40  
    41  	return nil, fmt.Errorf("failed to init fileProcessor,%s is not register", appFile.Strategy)
    42  }
    43  
    44  // overWriteProcessor :this will overwrite the FilePath with the Values.
    45  type overWriteProcessor struct {
    46  	v2.AppFile
    47  }
    48  
    49  func (r overWriteProcessor) Process(appRoot string) error {
    50  	target := filepath.Join(appRoot, r.Path)
    51  
    52  	logrus.Debugf("will do overwrite processor on the file : %s", target)
    53  	err := osUtils.NewCommonWriter(target).WriteFile([]byte(r.Data))
    54  	if err != nil {
    55  		return fmt.Errorf("failed to write to file %s with raw mode: %v", target, err)
    56  	}
    57  	return nil
    58  }
    59  
    60  // mergeProcessor :this will merge the FilePath with the Values.
    61  // Only files in yaml format are supported.
    62  // if Strategy is "merge" will deeply merge each yaml file section.
    63  type mergeProcessor struct {
    64  	v2.AppFile
    65  }
    66  
    67  func (m mergeProcessor) Process(appRoot string) error {
    68  	var (
    69  		result     [][]byte
    70  		srcDataMap = make(map[string]interface{})
    71  	)
    72  
    73  	err := yaml.Unmarshal([]byte(m.Data), &srcDataMap)
    74  	if err != nil {
    75  		return fmt.Errorf("failed to load config data: %v", err)
    76  	}
    77  
    78  	target := filepath.Join(appRoot, m.Path)
    79  
    80  	logrus.Debugf("will do merge processor on the file : %s", target)
    81  
    82  	f, err := os.Open(filepath.Clean(target))
    83  	if err != nil {
    84  		return err
    85  	}
    86  
    87  	dec := yaml.NewDecoder(f)
    88  	for {
    89  		destDataMap := make(map[string]interface{})
    90  		err = dec.Decode(destDataMap)
    91  		if errors.Is(err, io.EOF) {
    92  			break
    93  		}
    94  		if err != nil {
    95  			return fmt.Errorf("failed to unmarshal config data: %v", err)
    96  		}
    97  
    98  		err = mergo.Merge(&destDataMap, &srcDataMap, mergo.WithOverride)
    99  		if err != nil {
   100  			return fmt.Errorf("failed to merge config: %v", err)
   101  		}
   102  
   103  		out, err := yaml.Marshal(destDataMap)
   104  		if err != nil {
   105  			return err
   106  		}
   107  
   108  		result = append(result, out)
   109  	}
   110  
   111  	err = osUtils.NewCommonWriter(target).WriteFile(bytes.Join(result, []byte("\n---\n")))
   112  	if err != nil {
   113  		return fmt.Errorf("failed to write to file %s with raw mode: %v", target, err)
   114  	}
   115  	return nil
   116  }
   117  
   118  // envRender :this will render the FilePath with the Values.
   119  type envRender struct {
   120  	envData map[string]string
   121  }
   122  
   123  func (e envRender) Process(appRoot string) error {
   124  	if len(e.envData) == 0 {
   125  		return nil
   126  	}
   127  
   128  	logrus.Debugf("will render the dir : %s with the values: %+v\n", appRoot, e.envData)
   129  
   130  	return env.RenderTemplate(appRoot, e.envData)
   131  }