Example #1
0
File: app.go Project: mbrodala/otto
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
}
Example #2
0
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
}
Example #3
0
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
}
Example #4
0
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
}