github.com/leowmjw/otto@v0.2.1-0.20160126165905-6400716cf085/builtin/app/custom/app.go (about)

     1  package custom
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  	"strings"
     9  
    10  	"github.com/hashicorp/otto/app"
    11  	"github.com/hashicorp/otto/appfile"
    12  	"github.com/hashicorp/otto/foundation"
    13  	"github.com/hashicorp/otto/helper/bindata"
    14  	"github.com/hashicorp/otto/helper/compile"
    15  	"github.com/hashicorp/otto/helper/oneline"
    16  	"github.com/hashicorp/otto/helper/packer"
    17  	"github.com/hashicorp/otto/helper/schema"
    18  	"github.com/hashicorp/otto/helper/terraform"
    19  	"github.com/hashicorp/otto/helper/vagrant"
    20  )
    21  
    22  //go:generate go-bindata -pkg=custom -nomemcopy -nometadata ./data/...
    23  
    24  // App is an implementation of app.App
    25  type App struct{}
    26  
    27  func (a *App) Meta() (*app.Meta, error) {
    28  	return Meta, nil
    29  }
    30  
    31  func (a *App) Implicit(ctx *app.Context) (*appfile.File, error) {
    32  	return nil, nil
    33  }
    34  
    35  func (a *App) Compile(ctx *app.Context) (*app.CompileResult, error) {
    36  	fragmentPath := filepath.Join(ctx.Dir, "dev-dep", "Vagrantfile.fragment")
    37  
    38  	var opts compile.AppOptions
    39  	custom := &customizations{Opts: &opts}
    40  	opts = compile.AppOptions{
    41  		Ctx: ctx,
    42  		Bindata: &bindata.Data{
    43  			Asset:    Asset,
    44  			AssetDir: AssetDir,
    45  			Context: map[string]interface{}{
    46  				"fragment_path": fragmentPath,
    47  			},
    48  		},
    49  		FoundationConfig: foundation.Config{
    50  			ServiceName: ctx.Application.Name,
    51  		},
    52  		Customization: &compile.Customization{
    53  			Callback: custom.process,
    54  			Schema: map[string]*schema.FieldSchema{
    55  				"dev_vagrantfile": &schema.FieldSchema{
    56  					Type:        schema.TypeString,
    57  					Description: "Path to Vagrantfile",
    58  				},
    59  
    60  				"dep_vagrantfile": &schema.FieldSchema{
    61  					Type:        schema.TypeString,
    62  					Description: "Path to Vagrantfile template",
    63  				},
    64  
    65  				"packer": &schema.FieldSchema{
    66  					Type:        schema.TypeString,
    67  					Description: "Path to Packer template",
    68  				},
    69  
    70  				"terraform": &schema.FieldSchema{
    71  					Type:        schema.TypeString,
    72  					Description: "Path to a Terraform module",
    73  				},
    74  			},
    75  		},
    76  	}
    77  
    78  	return compile.App(&opts)
    79  }
    80  
    81  func (a *App) Build(ctx *app.Context) error {
    82  	// Determine if we set a Packer path.
    83  	path := filepath.Join(ctx.Dir, "build", "packer_path")
    84  	if _, err := os.Stat(path); err != nil {
    85  		if os.IsNotExist(err) {
    86  			return errors.New(strings.TrimSpace(errPackerNotSet))
    87  		}
    88  
    89  		return err
    90  	}
    91  
    92  	// Read the actual Packer dir
    93  	packerPath, err := oneline.Read(path)
    94  	if err != nil {
    95  		return fmt.Errorf(
    96  			"Error reading the Packer template path: %s\n\n"+
    97  				"An Otto recompile with `otto compile` usually fixes this.",
    98  			err)
    99  	}
   100  
   101  	return packer.Build(ctx, &packer.BuildOptions{
   102  		Dir:          filepath.Dir(packerPath),
   103  		TemplatePath: packerPath,
   104  	})
   105  }
   106  
   107  func (a *App) Deploy(ctx *app.Context) error {
   108  	// Determine if we set a Terraform path. If we didn't, then
   109  	// tell the user we can't deploy.
   110  	path := filepath.Join(ctx.Dir, "deploy", "terraform_path")
   111  	if _, err := os.Stat(path); err != nil {
   112  		if os.IsNotExist(err) {
   113  			return errors.New(strings.TrimSpace(errTerraformNotSet))
   114  		}
   115  
   116  		return err
   117  	}
   118  
   119  	// Read the actual TF dir
   120  	tfdir, err := oneline.Read(path)
   121  	if err != nil {
   122  		return fmt.Errorf(
   123  			"Error reading the Terraform module directory: %s\n\n"+
   124  				"An Otto recompile with `otto compile` usually fixes this.",
   125  			err)
   126  	}
   127  
   128  	// Determine if we set a Packer path. If we didn't, we disable
   129  	// the build loading for deploys.
   130  	disableBuild := true
   131  	path = filepath.Join(ctx.Dir, "build", "packer_path")
   132  	if _, err := os.Stat(path); err == nil {
   133  		disableBuild = false
   134  	}
   135  
   136  	// But if we did, then deploy using Terraform
   137  	return terraform.Deploy(&terraform.DeployOptions{
   138  		Dir:          tfdir,
   139  		DisableBuild: disableBuild,
   140  	}).Route(ctx)
   141  }
   142  
   143  func (a *App) Dev(ctx *app.Context) error {
   144  	// Determine if we have a Vagrant path set...
   145  	instructions := devInstructionsCustom
   146  	path := filepath.Join(ctx.Dir, "dev", "vagrant_path")
   147  	if _, err := os.Stat(path); err != nil {
   148  		if !os.IsNotExist(err) {
   149  			return err
   150  		}
   151  
   152  		path = ""
   153  	}
   154  	if path != "" {
   155  		var err error
   156  		path, err = oneline.Read(path)
   157  		if err != nil {
   158  			return fmt.Errorf(
   159  				"Error reading the Vagrant directory: %s\n\n"+
   160  					"An Otto recompile with `otto compile` usually fixes this.",
   161  				err)
   162  		}
   163  	}
   164  
   165  	if path == "" {
   166  		// Determine if we have our own Vagrantfile
   167  		path = filepath.Join(ctx.Dir, "dev", "Vagrantfile")
   168  		if _, err := os.Stat(path); err != nil {
   169  			if os.IsNotExist(err) {
   170  				return errors.New(strings.TrimSpace(errVagrantNotSet))
   171  			}
   172  
   173  			return err
   174  		}
   175  
   176  		instructions = devInstructionsDevDep
   177  	}
   178  
   179  	return vagrant.Dev(&vagrant.DevOptions{
   180  		Dir:          filepath.Dir(path),
   181  		Instructions: strings.TrimSpace(instructions),
   182  	}).Route(ctx)
   183  }
   184  
   185  func (a *App) DevDep(dst, src *app.Context) (*app.DevDep, error) {
   186  	// Determine if we have a Vagrantfile. This is a sentinel that
   187  	// we set this setting.
   188  	path := filepath.Join(src.Dir, "dev", "Vagrantfile")
   189  	if _, err := os.Stat(path); err != nil {
   190  		if os.IsNotExist(err) {
   191  			return nil, errors.New(strings.TrimSpace(errVagrantNotSet))
   192  		}
   193  
   194  		return nil, err
   195  	}
   196  
   197  	// We purposely return nil here. We don't need to do anything. It
   198  	// is all already setup from the compilation step.
   199  	return nil, nil
   200  }
   201  
   202  const devInstructionsCustom = `
   203  Vagrant was executed in the directory specified by the "dev"
   204  customization.
   205  `
   206  
   207  const devInstructionsDevDep = `
   208  A development has been created.
   209  
   210  Note that this development environment is just an example of what a
   211  consumer of this application might see as a development dependency.
   212  "Custom" types are not meant to be mutably developed like normal
   213  applications.
   214  `
   215  
   216  const errPackerNotSet = `
   217  Otto can't build this application because the "packer" setting
   218  isn't set in the "build" customization.
   219  
   220  For the "custom" application type, the "build" customization must
   221  set the "packer" setting to point to a Packer template to execute.
   222  Otto will execute this for the build.
   223  
   224  Example:
   225  
   226      customization "build" {
   227          packer = "path/to/template.json"
   228      }
   229  
   230  `
   231  
   232  const errTerraformNotSet = `
   233  Otto can't deploy this application because the "terraform" setting
   234  hasn't been set in the "deploy" customization.
   235  
   236  For the "custom" application type, the "deploy" customization must
   237  set the "terraform" setting to point to a Terraform module to execute.
   238  Otto will execute this module for the deploy.
   239  
   240  Example:
   241  
   242      customization "deploy" {
   243          terraform = "path/to/module"
   244      }
   245  
   246  `
   247  
   248  const errVagrantNotSet = `
   249  Otto can't build a development environment for this because the
   250  "vagrant" setting hasn't been set in the "dev" or "dev-dep" customization.
   251  
   252  For the "custom" application type, customizations must be used to
   253  tell Otto what to do. For the dev command, Otto requires either
   254  the "dev" or "dev-dep" customization to be set with the "vagrant" setting.
   255  The "vagrant" setting depends on which customization is being set. Please
   256  refer to the documentation for more details.
   257  
   258  Example:
   259  
   260      customization "dev" {
   261          vagrantfile = "path/to/Vagrantfile"
   262      }
   263  
   264  `