func (a *App) Implicit(ctx *app.Context) (*appfile.File, error) { // For Wordpress we implicitly depend on MySQL var result appfile.File if ctx.Tuple.App == "wordpress" { result.Application = &appfile.Application{ Dependencies: []*appfile.Dependency{ &appfile.Dependency{ Source: "github.com/hashicorp/otto/examples/mysql", }, }, } } return &result, nil }
func (c *Core) appContext(f *appfile.File) (*app.Context, error) { // Whether or not this is the root Appfile root := f.ID == c.appfile.ID // We need the configuration for the active infrastructure // so that we can build the tuple below config := f.ActiveInfrastructure() if config == nil { return nil, fmt.Errorf( "infrastructure not found in appfile: %s", f.Project.Infrastructure) } // The tuple we're looking for is the application type, the // infrastructure type, and the infrastructure flavor. Build that // tuple. tuple := app.Tuple{ App: f.Application.Type, Infra: config.Type, InfraFlavor: config.Flavor, } // The output directory for data. This is either the main app so // it goes directly into "app" or it is a dependency and goes into // a dep folder. outputDir := filepath.Join(c.compileDir, "app") if !root { outputDir = filepath.Join( c.compileDir, fmt.Sprintf("dep-%s", f.ID)) } // The cache directory for this app cacheDir := filepath.Join(c.dataDir, "cache", f.ID) if err := os.MkdirAll(cacheDir, 0755); err != nil { return nil, fmt.Errorf( "error making cache directory '%s': %s", cacheDir, err) } // The directory for global data globalDir := filepath.Join(c.dataDir, "global-data") if err := os.MkdirAll(globalDir, 0755); err != nil { return nil, fmt.Errorf( "error making global data directory '%s': %s", globalDir, err) } // Build the contexts for the foundations. We use this // to also compile the list of foundation dirs. foundationDirs := make([]string, len(config.Foundations)) for i, f := range config.Foundations { foundationDirs[i] = filepath.Join( outputDir, fmt.Sprintf("foundation-%s", f.Name)) } // Get the dev IP address ipDB := &localaddr.CachedDB{ DB: &localaddr.DB{Path: filepath.Join(c.dataDir, "ip.db")}, CachePath: filepath.Join(c.localDir, "dev_ip"), } ip, err := ipDB.IP() if err != nil { return nil, fmt.Errorf( "Error retrieving dev IP address: %s", err) } // Get the metadata var compileResult *app.CompileResult md, err := c.compileMetadata() if err != nil { return nil, fmt.Errorf( "Error loading compilation metadata: %s", err) } if md != nil { if root { compileResult = md.App } else { compileResult = md.AppDeps[f.ID] } } return &app.Context{ CompileResult: compileResult, Dir: outputDir, CacheDir: cacheDir, LocalDir: c.localDir, GlobalDir: globalDir, Tuple: tuple, Application: f.Application, DevIPAddress: ip.String(), Shared: context.Shared{ Appfile: f, FoundationDirs: foundationDirs, InstallDir: filepath.Join(c.dataDir, "binaries"), Directory: c.dir, Ui: c.ui, }, }, nil }
func (l *Loader) Load(f *appfile.File, dir string) (*appfile.File, error) { realFile := f // Load the "detected" Appfile appDef, err := appfile.Default(dir, l.Detector) if err != nil { return nil, fmt.Errorf("Error detecting app type: %s", err) } // Merge the detected Appfile with the real Appfile var merged appfile.File if err := merged.Merge(appDef); err != nil { return nil, fmt.Errorf("Error loading Appfile: %s", err) } if realFile != nil { if err := merged.Merge(realFile); err != nil { return nil, fmt.Errorf("Error loading Appfile: %s", err) } } realFile = &merged // If we have no application type, there is nothing more to do if realFile == nil || realFile.Application.Type == "" { return realFile, nil } // If we're not configured to do any further detection, return it if !realFile.Application.Detect { return realFile, nil } // Minimally compile the file that we can use to create a core compiled, err := l.Compiler.MinCompile(realFile) if err != nil { return nil, err } // Create a temporary directory we use for the core td, err := ioutil.TempDir("", "otto") if err != nil { return nil, err } defer os.RemoveAll(td) // Create a core core, err := otto.NewCore(&otto.CoreConfig{ DataDir: filepath.Join(td, "data"), LocalDir: filepath.Join(td, "local"), CompileDir: filepath.Join(td, "compile"), Appfile: compiled, Apps: l.Apps, }) if err != nil { return nil, err } // Get the app implementation appImpl, appCtx, err := core.App() if err != nil { return nil, err } defer app.Close(appImpl) // Load the implicit Appfile implicit, err := appImpl.Implicit(appCtx) if err != nil { return nil, err } var final appfile.File if err := final.Merge(appDef); err != nil { return nil, fmt.Errorf("Error loading Appfile: %s", err) } if implicit != nil { if err := final.Merge(implicit); err != nil { return nil, fmt.Errorf("Error loading Appfile: %s", err) } } if f != nil { if err := final.Merge(f); err != nil { return nil, fmt.Errorf("Error loading Appfile: %s", err) } } return &final, nil }
func (c *CompileCommand) Run(args []string) int { var flagAppfile string fs := c.FlagSet("compile", FlagSetNone) fs.Usage = func() { c.Ui.Error(c.Help()) } fs.StringVar(&flagAppfile, "appfile", "", "") if err := fs.Parse(args); err != nil { return 1 } // Load a UI ui := c.OttoUi() ui.Header("Loading Appfile...") // Determine all the Appfile paths // // First, if an Appfile was specified on the command-line, it must // exist so we validate that it exists. if flagAppfile != "" { fi, err := os.Stat(flagAppfile) if err != nil { c.Ui.Error(fmt.Sprintf( "Error loading Appfile: %s", err)) return 1 } if fi.IsDir() { flagAppfile = filepath.Join(flagAppfile, DefaultAppfile) } } // If the Appfile is still blank, just use our current directory if flagAppfile == "" { var err error flagAppfile, err = os.Getwd() if err != nil { c.Ui.Error(fmt.Sprintf( "Error loading working directory: %s", err)) return 1 } flagAppfile = filepath.Join(flagAppfile, DefaultAppfile) } // If we have the Appfile, then make sure it is an absoute path if flagAppfile != "" { var err error flagAppfile, err = filepath.Abs(flagAppfile) if err != nil { c.Ui.Error(fmt.Sprintf( "Error getting Appfile path: %s", err)) return 1 } } // Load the appfile. This is the only time we ever load the // raw Appfile. All other commands load the compiled Appfile. var app *appfile.File if fi, err := os.Stat(flagAppfile); err == nil && !fi.IsDir() { app, err = appfile.ParseFile(flagAppfile) if err != nil { c.Ui.Error(err.Error()) return 1 } } // Tell the user what is happening if they have no Appfile if app == nil { ui.Header("No Appfile found! Detecting project information...") ui.Message(fmt.Sprintf( "No Appfile was found. If there is no Appfile, Otto will do its best\n" + "to detect the type of application this is and set reasonable defaults.\n" + "This is a good way to get started with Otto, but over time we recommend\n" + "writing a real Appfile since this will allow more complex customizations,\n" + "the ability to reference dependencies, versioning, and more.")) } // Parse the detectors dataDir, err := c.DataDir() if err != nil { c.Ui.Error(err.Error()) return 1 } detectorDir := filepath.Join(dataDir, DefaultLocalDataDetectorDir) log.Printf("[DEBUG] loading detectors from: %s", detectorDir) detectConfig, err := detect.ParseDir(detectorDir) if err != nil { c.Ui.Error(err.Error()) return 1 } if detectConfig == nil { detectConfig = &detect.Config{} } err = detectConfig.Merge(&detect.Config{Detectors: c.Detectors}) if err != nil { c.Ui.Error(err.Error()) return 1 } // Load the default Appfile so we can merge in any defaults into // the loaded Appfile (if there is one). appDef, err := appfile.Default(filepath.Dir(flagAppfile), detectConfig) if err != nil { c.Ui.Error(fmt.Sprintf( "Error loading Appfile: %s", err)) return 1 } // If there was no loaded Appfile and we don't have an application // type then we weren't able to detect the type. Error. if app == nil && appDef.Application.Type == "" { c.Ui.Error(strings.TrimSpace(errCantDetectType)) return 1 } // Merge the appfiles if app != nil { if err := appDef.Merge(app); err != nil { c.Ui.Error(fmt.Sprintf( "Error loading Appfile: %s", err)) return 1 } } app = appDef // Compile the Appfile ui.Header("Fetching all Appfile dependencies...") capp, err := appfile.Compile(app, &appfile.CompileOpts{ Dir: filepath.Join( filepath.Dir(app.Path), DefaultOutputDir, DefaultOutputDirCompiledAppfile), Detect: detectConfig, Callback: c.compileCallback(ui), }) if err != nil { c.Ui.Error(fmt.Sprintf( "Error compiling Appfile: %s", err)) return 1 } // Get a core core, err := c.Core(capp) if err != nil { c.Ui.Error(fmt.Sprintf( "Error loading core: %s", err)) return 1 } // Get the active infrastructure just for UI reasons infra := app.ActiveInfrastructure() // Before the compilation, output to the user what is going on ui.Header("Compiling...") ui.Message(fmt.Sprintf( "Application: %s (%s)", app.Application.Name, app.Application.Type)) ui.Message(fmt.Sprintf("Project: %s", app.Project.Name)) ui.Message(fmt.Sprintf( "Infrastructure: %s (%s)", infra.Type, infra.Flavor)) ui.Message("") // Compile! if err := core.Compile(); err != nil { c.Ui.Error(fmt.Sprintf( "Error compiling: %s", err)) return 1 } // Success! ui.Header("[green]Compilation success!") ui.Message(fmt.Sprintf( "[green]This means that Otto is now ready to start a development environment,\n" + "deploy this application, build the supporting infastructure, and\n" + "more. See the help for more information.\n\n" + "Supporting files to enable Otto to manage your application from\n" + "development to deployment have been placed in the output directory.\n" + "These files can be manually inspected to determine what Otto will do.")) return 0 }