func (c ProjectCompiler) Compile(projectPath *string, dataFile *string, baseDir *string, workDir *string) { log.Notice("▶ Loading environment data from file:\n %s", *projectPath) environment, source, err := loadEnvironment(*projectPath) if err != nil { util.HandleFatalError(err) } for _, provider := range environment.Providers { log.Notice("▶ Switching provider to: %s", provider.Name) log.Debug(" Creating template context") if context, err := createContext(&environment.Data, &provider, dataFile); err == nil { log.Notice("▶ Context data:") printContextData(context, false) log.Notice("▶ Compiling artifacts") if compiled, err := compileTemplateFromString(context, source); err == nil { var project data.Project if err := util.LoadYAMLFromString(*compiled, &project); err == nil { // Compile each artifact for _, artifact := range project.Artifacts { //log.Info(" Compiling:\n artifact: %s\n provider: %s", artifact.Name, provider.Name) if err := compileArtifact(context, &artifact, provider.Name, baseDir, workDir); err != nil { util.HandleFatalError(err) } } } else { err := errors.New(fmt.Sprintf("Error parsing project artifacts\nerror:\n %s", err.Error())) util.HandleFatalError(err) } } else { util.HandleFatalError(err) } } else { util.HandleFatalError(err) } } log.Notice("▶ Completed successfully") }
func validateAndRunBuildCommand(flagProjectFile string, flagDataFile string) error { // Validate command if len(strings.TrimSpace(flagProjectFile)) == 0 { return errors.New("Error: missing project file") } // Check if project file exists if _, err := os.Stat(flagProjectFile); os.IsNotExist(err) { return errors.New("Error: Project file doesn't exist") } // Workdir workDir, _ := os.Getwd() workDir, _ = filepath.Abs(workDir) log.Debug("Work dir: %s", workDir) // Get absolute paths var projectRootDir string if projectAbsolutePath, err := filepath.Abs(flagProjectFile); err == nil { projectRootDir = filepath.Dir(projectAbsolutePath) log.Debug("Project root:\n %s", projectRootDir) log.Debug("Project file:\n %s", projectAbsolutePath) var dataAbsolutePath *string if len(flagDataFile) > 0 { if dataPath, err := filepath.Abs(flagDataFile); err == nil { dataAbsolutePath = &dataPath log.Debug("Extra data file:\n %s", dataAbsolutePath) } else { return err } } log.Notice("▶ Building project from file:\n %s", projectAbsolutePath) compiler.ProjectCompiler{}.Compile(&projectAbsolutePath, dataAbsolutePath, &projectRootDir, &workDir) return nil } else { return err } }
func compileArtifact(context *pongo2.Context, artifact *data.Artifact, providerName string, baseDir *string, workDir *string) error { outputFile := filepath.Join(*workDir, "build", providerName, artifact.Name, "user-data") outputDir := filepath.Dir(outputFile) // Create output directory if missing if exists, _ := util.FileExists(outputDir); !exists { if err := os.MkdirAll(outputDir, 0755); err != nil { return errors.New(fmt.Sprintf("Could not create output directory:\n %s\n\nnested error:\n %s", outputDir, err.Error())) } } log.Notice("▶ Compiling artifact [%s] to:\n %s", artifact.Name, outputFile) log.Debug(" Setting artifact name to the context") c := pongo2.Context{ "env": map[string]interface{}{ "provider": providerName, "artifact": artifact.Name, }, } if err := mergo.MergeWithOverwrite(context, c); err != nil { return err } printContextData(context, true) // Create the initial user data objects buffer := bytes.NewBufferString("#cloud-config\n---\n") writeFiles := make([]data.WriteFile, 0, 10) units := make([]data.Unit, 0, 10) // Process bricks bricks := artifact.Bricks for _, brick := range bricks { log.Info(" Processing brick:\n %s", brick) // Load metadata if metadata, brickBaseDir, err := loadBrickMetadata(context, baseDir, &brick); err == nil { // Process brick files log.Debug(" Processing brick files") if brickWriteFiles, err := processMetadataFiles(context, metadata, brickBaseDir); err != nil { return err } else { writeFiles = append(writeFiles, brickWriteFiles...) } // Process brick units log.Debug(" Processing brick unit") if brickUnit, err := processMetadataUnits(context, metadata, brickBaseDir); err != nil { return err } else if brickUnit != nil { units = append(units, *brickUnit) } } else { return err } } // Write compiled write-files section if len(writeFiles) > 0 { writeFilesMap := make(map[string]interface{}) writeFilesMap["write-files"] = writeFiles if output, err := yaml.Marshal(writeFilesMap); err != nil { return err } else { buffer.Write(output) } } // Write rest of config file cloudConfig := artifact.CloudConfig // Custom CoreOS if cloudConfig.CoreOS != nil { coreOs := cloudConfig.CoreOS // Add units // if coreOs["units"] != nil { coreOs["units"] = units // } // Write CoreOS section coreOsMap := make(map[string]interface{}) coreOsMap["coreos"] = coreOs if output, err := yaml.Marshal(coreOsMap); err != nil { return err } else { buffer.Write(output) } } // Append everything else to the end of the file if cloudConfig.Generic != nil { if output, err := yaml.Marshal(cloudConfig.Generic); err != nil { return err } else { buffer.Write(output) } } log.Info(" Writing output file to:\n %s", outputFile) if err := ioutil.WriteFile(outputFile, buffer.Bytes(), 0755); err != nil { return err } return nil }
func main() { var flagDebug bool var flagVerbose bool var flagQuiet bool var flagProjectFile string var flagDataFile string // Root var RootCommand = &cobra.Command{ Use: "magutt", Short: "Magutt is a simple tool to assemble cloud-config files for CoreOS", PersistentPreRun: func(cmd *cobra.Command, args []string) { logLevel := log.LevelInfo if flagQuiet && (flagDebug || flagVerbose) { log.Warn("I'm confused... How can I be verbose and quiet at the same time?!?!?!?") os.Exit(1) } else { if flagQuiet { logLevel = log.LevelOff } else { if flagDebug { logLevel = log.LevelDebug } // Verbose flag overrides debug level if flagVerbose { logLevel = log.LevelTrace } } } log.SetLogLevel(logLevel) }, } RootCommand.PersistentFlags().BoolVarP(&flagQuiet, "quiet", "q", false, "disable logging") RootCommand.PersistentFlags().BoolVarP(&flagDebug, "debug", "d", false, "enable debug logging") RootCommand.PersistentFlags().BoolVarP(&flagVerbose, "verbose", "v", false, "enable verbose logging") // Build var CmdBuild = &cobra.Command{ Use: "build", Short: "Build a config file", Run: func(cmd *cobra.Command, args []string) { if err := validateAndRunBuildCommand(flagProjectFile, flagDataFile); err != nil { cmd.Usage() util.HandleFatalError(err) } }, } CmdBuild.Flags().StringVarP(&flagProjectFile, "input", "i", "", "input project file") CmdBuild.Flags().StringVarP(&flagDataFile, "extra", "e", "", "extra data file") // Version var CmdVersion = &cobra.Command{ Use: "version", Short: "Print version number", Run: func(cmd *cobra.Command, args []string) { log.Notice("Magutt v%s\n", version) }, } // Common flags RootCommand.AddCommand(CmdBuild) RootCommand.AddCommand(CmdVersion) // Process command line RootCommand.Execute() }