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 }