Beispiel #1
0
// TestAppfile returns a compiled appfile for the given path. This uses
// defaults for detectors and such so it is up to you to use a fairly
// complete Appfile.
func TestAppfile(t TestT, path string) *appfile.Compiled {
	def, err := appfile.Default(filepath.Dir(path), &detect.Config{
		Detectors: []*detect.Detector{
			&detect.Detector{
				Type: "test",
				File: []string{"Appfile"},
			},
		},
	})
	if err != nil {
		t.Fatal("err: ", err)
	}

	// Default type should be "test"
	def.Infrastructure[0].Type = "test"
	def.Infrastructure[0].Flavor = "test"
	def.Infrastructure[0].Foundations = nil

	// Parse the raw file
	f, err := appfile.ParseFile(path)
	if err != nil {
		t.Fatal("err: ", err)
	}

	// Merge
	if err := def.Merge(f); err != nil {
		t.Fatal("err: ", err)
	}
	f = def

	// Create a temporary directory for the compilation data. We don't
	// delete this now in case we're using any of that data, but the
	// temp dir should get cleaned up by the system at some point.
	td, err := ioutil.TempDir("", "otto")
	if err != nil {
		t.Fatal("err: ", err)
	}

	// Compile it!
	compiler, err := appfile.NewCompiler(&appfile.CompileOpts{
		Dir: td,
	})
	if err != nil {
		t.Fatal("err: ", err)
	}
	result, err := compiler.Compile(f)
	if err != nil {
		t.Fatal("err: ", err)
	}

	return result
}
Beispiel #2
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
}
Beispiel #3
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 all the plugins, we use all the plugins for compilation only
	// so we have full access to detectors and app types.
	pluginMgr, err := c.PluginManager()
	if err != nil {
		c.Ui.Error(fmt.Sprintf(
			"Error initializing plugin manager: %s", err))
		return 1
	}
	if err := pluginMgr.LoadAll(); err != nil {
		c.Ui.Error(fmt.Sprintf(
			"Error loading plugins: %s", err))
		return 1
	}

	// Load the detectors from the plugins
	detectors := make([]*detect.Detector, 0, 20)
	detectors = append(detectors, c.Detectors...)
	for _, p := range pluginMgr.Plugins() {
		detectors = append(detectors, p.AppMeta.Detectors...)
	}

	// Load a UI
	ui := c.OttoUi()
	ui.Header("Loading Appfile...")

	app, appPath, err := loadAppfile(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: 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(appPath, 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
	}

	// Store the used plugins so later calls don't have to load everything
	usedPath, err := c.AppfilePluginsPath(capp)
	if err != nil {
		c.Ui.Error(fmt.Sprintf(
			"Error compiling: %s", err))
		return 1
	}
	if err := pluginMgr.StoreUsed(usedPath); err != nil {
		c.Ui.Error(fmt.Sprintf(
			"Error compiling plugin data: %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 infrastructure, 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
}
Beispiel #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
}
Beispiel #5
0
func TestLoader_basic(t *testing.T) {
	cases := []struct {
		Path            string
		Input, Expected *appfile.File
	}{
		{
			"basic",
			&appfile.File{
				Application: &appfile.Application{
					Name: "foo",
				},
			},
			&appfile.File{
				Application: &appfile.Application{
					Name: "foo",
				},
			},
		},

		{
			"detect",
			&appfile.File{
				Application: &appfile.Application{
					Name:   "foo",
					Detect: true,
				},
			},
			&appfile.File{
				Application: &appfile.Application{
					Name: "foo",
					Type: "test",
					Dependencies: []*appfile.Dependency{
						&appfile.Dependency{Source: "tubes"},
					},
				},
			},
		},

		{
			"detect-no-appfile",
			nil,
			&appfile.File{
				Application: &appfile.Application{
					Type: "test",
					Dependencies: []*appfile.Dependency{
						&appfile.Dependency{Source: "tubes"},
					},
				},
			},
		},

		{
			"detect-false",
			&appfile.File{
				Application: &appfile.Application{
					Name:   "foo",
					Detect: false,
				},
			},
			&appfile.File{
				Application: &appfile.Application{
					Name:   "foo",
					Type:   "test",
					Detect: false,
				},
			},
		},
	}

	l, appMock := testLoader(t)
	appMock.ImplicitResult = &appfile.File{
		Application: &appfile.Application{
			Dependencies: []*appfile.Dependency{
				&appfile.Dependency{Source: "tubes"},
			},
		},
	}

	for _, tc := range cases {
		tc.Path = testPath(tc.Path)

		actual, err := l.Load(tc.Input, tc.Path)
		if err != nil {
			t.Fatalf("err: %s", err)
		}

		// Load the default and merge it
		def, err := appfile.Default(tc.Path, nil)
		if err != nil {
			t.Fatalf("err %s: %s", tc.Path, err)
		}
		if err := def.Merge(tc.Expected); err != nil {
			t.Fatalf("err %s: %s", tc.Path, err)
		}
		tc.Expected = def

		if !reflect.DeepEqual(actual, tc.Expected) {
			t.Fatalf("err: %s\n\n%#v", tc.Path, actual)
		}
	}
}