github.com/bhameyie/otto@v0.2.1-0.20160406174117-16052efa52ec/builtin/app/ruby/app.go (about) 1 package rubyapp 2 3 import ( 4 "fmt" 5 "log" 6 "path/filepath" 7 "strings" 8 9 "github.com/hashicorp/otto/app" 10 "github.com/hashicorp/otto/appfile" 11 rubySP "github.com/hashicorp/otto/builtin/scriptpack/ruby" 12 stdSP "github.com/hashicorp/otto/builtin/scriptpack/stdlib" 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 "github.com/hashicorp/otto/scriptpack" 21 ) 22 23 //go:generate go-bindata -pkg=rubyapp -nomemcopy -nometadata ./data/... 24 25 // App is an implementation of app.App 26 type App struct{} 27 28 func (a *App) Meta() (*app.Meta, error) { 29 return Meta, nil 30 } 31 32 func (a *App) Implicit(ctx *app.Context) (*appfile.File, error) { 33 // depMap is our mapping of gem to dependency URL 34 depMap := map[string]string{ 35 "dalli": "github.com/hashicorp/otto/examples/memcached", 36 "pg": "github.com/hashicorp/otto/examples/postgresql", 37 "redis": "github.com/hashicorp/otto/examples/redis", 38 } 39 40 // used keeps track of dependencies we've used so we don't 41 // double-up on dependencies 42 used := map[string]struct{}{} 43 44 // Get the path to the working directory 45 dir := filepath.Dir(ctx.Appfile.Path) 46 log.Printf("[DEBUG] app: implicit check path: %s", dir) 47 48 // If we have certain gems, add the dependencies 49 var deps []*appfile.Dependency 50 for k, v := range depMap { 51 // If we already used v, then don't do it 52 if _, ok := used[v]; ok { 53 continue 54 } 55 56 // If we don't have the gem, then nothing to do 57 log.Printf("[DEBUG] app: checking for Gem: %s", k) 58 ok, err := HasGem(dir, k) 59 if err != nil { 60 return nil, err 61 } 62 if !ok { 63 log.Printf("[DEBUG] app: Gem not found: %s", k) 64 continue 65 } 66 log.Printf("[INFO] app: found Gem '%s', adding dep: %s", k, v) 67 68 // We have it! Add the implicit 69 deps = append(deps, &appfile.Dependency{ 70 Source: v, 71 }) 72 used[v] = struct{}{} 73 } 74 75 // Build an implicit Appfile if we have deps 76 var result *appfile.File 77 if len(deps) > 0 { 78 result = &appfile.File{ 79 Application: &appfile.Application{ 80 Dependencies: deps, 81 }, 82 } 83 } 84 85 return result, nil 86 } 87 88 func (a *App) Compile(ctx *app.Context) (*app.CompileResult, error) { 89 var opts compile.AppOptions 90 custom := &customizations{Opts: &opts} 91 opts = compile.AppOptions{ 92 Ctx: ctx, 93 Result: &app.CompileResult{ 94 Version: 1, 95 }, 96 Bindata: &bindata.Data{ 97 Asset: Asset, 98 AssetDir: AssetDir, 99 Context: map[string]interface{}{}, 100 }, 101 ScriptPacks: []*scriptpack.ScriptPack{ 102 &stdSP.ScriptPack, 103 &rubySP.ScriptPack, 104 }, 105 Customization: (&compile.Customization{ 106 Callback: custom.process, 107 Schema: map[string]*schema.FieldSchema{ 108 "ruby_version": &schema.FieldSchema{ 109 Type: schema.TypeString, 110 Default: "detect", 111 Description: "Ruby version to install", 112 }, 113 }, 114 }).Merge(compile.VagrantCustomizations(&opts)), 115 } 116 117 return compile.App(&opts) 118 } 119 120 func (a *App) Build(ctx *app.Context) error { 121 return packer.Build(ctx, &packer.BuildOptions{ 122 InfraOutputMap: map[string]string{ 123 "region": "aws_region", 124 "vpc_id": "aws_vpc_id", 125 "subnet_public": "aws_subnet_id", 126 }, 127 }) 128 } 129 130 func (a *App) Deploy(ctx *app.Context) error { 131 return terraform.Deploy(&terraform.DeployOptions{ 132 InfraOutputMap: map[string]string{ 133 "region": "aws_region", 134 "subnet-private": "private_subnet_id", 135 "subnet-public": "public_subnet_id", 136 }, 137 }).Route(ctx) 138 } 139 140 func (a *App) Dev(ctx *app.Context) error { 141 var layered *vagrant.Layered 142 143 // We only setup a layered environment if we've recompiled since 144 // version 0. If we're still at version 0 then we have to use the 145 // non-layered dev environment. 146 if ctx.CompileResult.Version > 0 { 147 // Read the go version, since we use that for our layer 148 version, err := oneline.Read(filepath.Join(ctx.Dir, "dev", "ruby_version")) 149 if err != nil { 150 return err 151 } 152 153 // Setup layers 154 layered, err = vagrant.DevLayered(ctx, []*vagrant.Layer{ 155 &vagrant.Layer{ 156 ID: fmt.Sprintf("ruby%s", version), 157 Vagrantfile: filepath.Join(ctx.Dir, "dev", "layer-base", "Vagrantfile"), 158 }, 159 }) 160 if err != nil { 161 return err 162 } 163 } 164 165 // Build the actual development environment 166 return vagrant.Dev(&vagrant.DevOptions{ 167 Instructions: strings.TrimSpace(devInstructions), 168 Layer: layered, 169 }).Route(ctx) 170 } 171 172 func (a *App) DevDep(dst, src *app.Context) (*app.DevDep, error) { 173 return vagrant.DevDep(dst, src, &vagrant.DevDepOptions{}) 174 } 175 176 const devInstructions = ` 177 A development environment has been created for writing a generic 178 Ruby-based app. 179 180 Ruby is pre-installed. To work on your project, edit files locally on your 181 own machine. The file changes will be synced to the development environment. 182 183 When you're ready to build your project, run 'otto dev ssh' to enter 184 the development environment. You'll be placed directly into the working 185 directory where you can run 'bundle' and 'ruby' as you normally would. 186 187 You can access any running web application using the IP above. 188 `