github.com/microsoft/fabrikate@v1.0.0-alpha.1.0.20210115014322-dc09194d0885/internal/generators/static.go (about)

     1  package generators
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"io/ioutil"
     7  	"net/http"
     8  	"os"
     9  	"path"
    10  	"path/filepath"
    11  	"strings"
    12  
    13  	"github.com/kyokomi/emoji"
    14  	"github.com/microsoft/fabrikate/internal/core"
    15  	"github.com/microsoft/fabrikate/internal/logger"
    16  )
    17  
    18  // StaticGenerator uses a static directory of resource manifests to create a rolled up multi-part manifest.
    19  type StaticGenerator struct{}
    20  
    21  // GetStaticManifestsPath returns the path where a static components YAML
    22  // manifest files are located, defaulting `path` unless `method: http` in which
    23  // case `<path>/components/<name>` is used.
    24  func GetStaticManifestsPath(c core.Component) (string, error) {
    25  	// if strings.EqualFold(c.Method, "http") {
    26  	// 	return path.Join(c.PhysicalPath, "components", c.Name)
    27  	// }
    28  
    29  	// return path.Join(c.PhysicalPath, c.Path)
    30  
    31  	i, err := c.ToInstallable()
    32  	if err != nil {
    33  		return "", err
    34  	}
    35  	installPath, err := i.GetInstallPath()
    36  	if err != nil {
    37  		return "", err
    38  	}
    39  
    40  	return installPath, nil
    41  }
    42  
    43  // Generate iterates a static directory of resource manifests and creates a multi-part manifest.
    44  func (sg *StaticGenerator) Generate(component *core.Component) (manifest string, err error) {
    45  	logger.Info(emoji.Sprintf(":truck: Generating component '%s' statically from path %s", component.Name, component.Path))
    46  
    47  	staticPath, err := GetStaticManifestsPath(*component)
    48  	if err != nil {
    49  		return "", err
    50  	}
    51  	staticFiles, err := ioutil.ReadDir(staticPath)
    52  	if err != nil {
    53  		logger.Error(fmt.Sprintf("error reading from directory %s", staticPath))
    54  		return "", err
    55  	}
    56  
    57  	manifests := ""
    58  	for _, staticFile := range staticFiles {
    59  		staticFilePath := path.Join(staticPath, staticFile.Name())
    60  
    61  		staticFileManifest, err := ioutil.ReadFile(staticFilePath)
    62  		if err != nil {
    63  			return "", err
    64  		}
    65  
    66  		manifests += fmt.Sprintf("---\n%s\n", staticFileManifest)
    67  	}
    68  
    69  	return manifests, err
    70  }
    71  
    72  // Install for StaticGenerator gives the ability to point to a single yaml
    73  // manifest over `method: http`; This is a noop for any all other methods.
    74  func (sg *StaticGenerator) Install(c *core.Component) (err error) {
    75  	if strings.EqualFold(c.Method, "http") {
    76  		// validate that `Source` points to a yaml file
    77  		validSourceExtensions := []string{".yaml", ".yml"}
    78  		isValidExtension := func() bool {
    79  			sourceExt := strings.ToLower(filepath.Ext(c.Source))
    80  			for _, validExt := range validSourceExtensions {
    81  				if sourceExt == validExt {
    82  					return true
    83  				}
    84  			}
    85  			return false
    86  		}()
    87  		if !isValidExtension {
    88  			return fmt.Errorf("source for 'static' component '%s' must end in one of %v; given: '%s'", c.Name, validSourceExtensions, c.Source)
    89  		}
    90  
    91  		response, err := http.Get(c.Source)
    92  		if err != nil {
    93  			return err
    94  		}
    95  		defer response.Body.Close()
    96  
    97  		componentsPath := path.Join(c.PhysicalPath, "components", c.Name)
    98  		if err := os.MkdirAll(componentsPath, 0777); err != nil {
    99  			return err
   100  		}
   101  
   102  		// Write the downloaded resource manifest file
   103  		out, err := os.Create(path.Join(componentsPath, c.Name+".yaml"))
   104  		if err != nil {
   105  			logger.Error(emoji.Sprintf(":no_entry_sign: Error occurred in install for component '%s'\nError: %s", c.Name, err))
   106  			return err
   107  		}
   108  		defer out.Close()
   109  
   110  		if _, err = io.Copy(out, response.Body); err != nil {
   111  			logger.Error(emoji.Sprintf(":no_entry_sign: Error occurred in writing manifest file for component '%s'\nError: %s", c.Name, err))
   112  			return err
   113  		}
   114  	}
   115  
   116  	return nil
   117  }