Пример #1
0
func newDeployOpts(ctx context.Context, w http.ResponseWriter, req *http.Request) (*empire.DeployOpts, error) {
	var form PostDeployForm

	if err := Decode(req, &form); err != nil {
		return nil, err
	}

	m, err := findMessage(req)
	if err != nil {
		return nil, err
	}

	w.Header().Set("Content-Type", "application/json; boundary=NL")

	if form.Image.Tag == "" && form.Image.Digest == "" {
		form.Image.Tag = "latest"
	}

	opts := empire.DeployOpts{
		User:    auth.UserFromContext(ctx),
		Image:   form.Image,
		Output:  empire.NewDeploymentStream(streamhttp.StreamingResponseWriter(w)),
		Message: m,
		Stream:  form.Stream,
	}
	return &opts, nil
}
Пример #2
0
// Deploy builds/determines the docker image to deploy, then deploys it with
// Empire.
func (d *EmpireDeployer) Deploy(ctx context.Context, event events.Deployment, w io.Writer) error {
	img, err := d.BuildImage(ctx, w, event)
	if err != nil {
		return err
	}

	// What we write to w should be plain text. `p` will get the jsonmessage
	// stream.
	p := dockerutil.DecodeJSONMessageStream(w)

	message := event.Deployment.Description
	if message == "" {
		message = fmt.Sprintf("GitHub deployment #%d of %s", event.Deployment.ID, event.Repository.FullName)
	}
	_, err = d.empire.Deploy(ctx, empire.DeployOpts{
		Image:   img,
		Output:  empire.NewDeploymentStream(p),
		User:    &empire.User{Name: event.Deployment.Creator.Login},
		Stream:  true,
		Message: message,
	})
	if err != nil {
		return err
	}

	return p.Err()
}
Пример #3
0
func TestEmpire_Deploy_ImageNotFound(t *testing.T) {
	e := empiretest.NewEmpire(t)
	s := new(mockScheduler)
	e.Scheduler = s
	e.ProcfileExtractor = empire.ProcfileExtractorFunc(func(ctx context.Context, img image.Image, w io.Writer) ([]byte, error) {
		return nil, errors.New("image not found")
	})

	// Deploying an image to an app that doesn't exist will create a new
	// app.
	_, err := e.Deploy(context.Background(), empire.DeployOpts{
		User:   &empire.User{Name: "ejholmes"},
		Output: empire.NewDeploymentStream(ioutil.Discard),
		Image:  image.Image{Repository: "remind101/acme-inc"},
	})
	assert.Error(t, err)

	// If there's an error deploying, then the transaction should be rolled
	// backed and no apps should exist.
	apps, err := e.Apps(empire.AppsQuery{})
	assert.NoError(t, err)
	assert.Equal(t, 0, len(apps))

	s.AssertExpectations(t)
}
Пример #4
0
func TestEmpire_Deploy(t *testing.T) {
	e := empiretest.NewEmpire(t)
	s := new(mockScheduler)
	e.Scheduler = s

	user := &empire.User{Name: "ejholmes"}

	app, err := e.Create(context.Background(), empire.CreateOpts{
		User: user,
		Name: "acme-inc",
	})
	assert.NoError(t, err)

	img := image.Image{Repository: "remind101/acme-inc"}
	s.On("Submit", &scheduler.App{
		ID:      app.ID,
		Name:    "acme-inc",
		Release: "v1",
		Env: map[string]string{
			"EMPIRE_APPID":   app.ID,
			"EMPIRE_APPNAME": "acme-inc",
			"EMPIRE_RELEASE": "v1",
		},
		Labels: map[string]string{
			"empire.app.name":    "acme-inc",
			"empire.app.id":      app.ID,
			"empire.app.release": "v1",
		},
		Processes: []*scheduler.Process{
			{
				Type:        "scheduled",
				Image:       img,
				Command:     []string{"./bin/scheduled"},
				Schedule:    scheduler.CRONSchedule("* * * * * *"),
				Instances:   0,
				MemoryLimit: 536870912,
				CPUShares:   256,
				Nproc:       256,
				Env: map[string]string{
					"EMPIRE_PROCESS":       "scheduled",
					"EMPIRE_PROCESS_SCALE": "0",
					"SOURCE":               "acme-inc.scheduled.v1",
				},
				Labels: map[string]string{
					"empire.app.process": "scheduled",
				},
			},
			{
				Type:    "web",
				Image:   img,
				Command: []string{"./bin/web"},
				Exposure: &scheduler.Exposure{
					Type: &scheduler.HTTPExposure{},
				},
				Instances:   1,
				MemoryLimit: 536870912,
				CPUShares:   256,
				Nproc:       256,
				Env: map[string]string{
					"EMPIRE_PROCESS":       "web",
					"EMPIRE_PROCESS_SCALE": "1",
					"SOURCE":               "acme-inc.web.v1",
				},
				Labels: map[string]string{
					"empire.app.process": "web",
				},
			},
			{
				Type:        "worker",
				Image:       img,
				Command:     []string{"./bin/worker"},
				Instances:   0,
				MemoryLimit: 536870912,
				CPUShares:   256,
				Nproc:       256,
				Env: map[string]string{
					"EMPIRE_PROCESS":       "worker",
					"EMPIRE_PROCESS_SCALE": "0",
					"SOURCE":               "acme-inc.worker.v1",
				},
				Labels: map[string]string{
					"empire.app.process": "worker",
				},
			},
		},
	}).Return(nil)

	_, err = e.Deploy(context.Background(), empire.DeployOpts{
		App:    app,
		User:   user,
		Output: empire.NewDeploymentStream(ioutil.Discard),
		Image:  img,
	})
	assert.NoError(t, err)

	s.AssertExpectations(t)
}
Пример #5
0
func TestEmpire_Set(t *testing.T) {
	e := empiretest.NewEmpire(t)
	s := new(mockScheduler)
	e.Scheduler = s
	e.ProcfileExtractor = empiretest.ExtractProcfile(procfile.ExtendedProcfile{
		"web": procfile.Process{
			Command: []string{"./bin/web"},
		},
	})

	user := &empire.User{Name: "ejholmes"}

	// Create an app
	app, err := e.Create(context.Background(), empire.CreateOpts{
		User: user,
		Name: "acme-inc",
	})
	assert.NoError(t, err)

	// Add some environment variables to it.
	prod := "production"
	_, err = e.Set(context.Background(), empire.SetOpts{
		User: user,
		App:  app,
		Vars: empire.Vars{
			"RAILS_ENV": &prod,
		},
	})
	assert.NoError(t, err)

	// Deploy a new image to the app.
	img := image.Image{Repository: "remind101/acme-inc"}
	s.On("Submit", &scheduler.App{
		ID:      app.ID,
		Name:    "acme-inc",
		Release: "v1",
		Env: map[string]string{
			"EMPIRE_APPID":   app.ID,
			"EMPIRE_APPNAME": "acme-inc",
			"EMPIRE_RELEASE": "v1",
			"RAILS_ENV":      "production",
		},
		Labels: map[string]string{
			"empire.app.name":    "acme-inc",
			"empire.app.id":      app.ID,
			"empire.app.release": "v1",
		},
		Processes: []*scheduler.Process{
			{
				Type:    "web",
				Image:   img,
				Command: []string{"./bin/web"},
				Exposure: &scheduler.Exposure{
					Type: &scheduler.HTTPExposure{},
				},
				Instances:   1,
				MemoryLimit: 536870912,
				CPUShares:   256,
				Nproc:       256,
				Env: map[string]string{
					"EMPIRE_PROCESS":       "web",
					"EMPIRE_PROCESS_SCALE": "1",
					"SOURCE":               "acme-inc.web.v1",
				},
				Labels: map[string]string{
					"empire.app.process": "web",
				},
			},
		},
	}).Once().Return(nil)

	_, err = e.Deploy(context.Background(), empire.DeployOpts{
		App:    app,
		User:   user,
		Output: empire.NewDeploymentStream(ioutil.Discard),
		Image:  img,
	})
	assert.NoError(t, err)

	// Remove the environment variable
	s.On("Submit", &scheduler.App{
		ID:      app.ID,
		Name:    "acme-inc",
		Release: "v2",
		Env: map[string]string{
			"EMPIRE_APPID":   app.ID,
			"EMPIRE_APPNAME": "acme-inc",
			"EMPIRE_RELEASE": "v2",
		},
		Labels: map[string]string{
			"empire.app.name":    "acme-inc",
			"empire.app.id":      app.ID,
			"empire.app.release": "v2",
		},
		Processes: []*scheduler.Process{
			{
				Type:    "web",
				Image:   img,
				Command: []string{"./bin/web"},
				Exposure: &scheduler.Exposure{
					Type: &scheduler.HTTPExposure{},
				},
				Instances:   1,
				MemoryLimit: 536870912,
				CPUShares:   256,
				Nproc:       256,
				Env: map[string]string{
					"EMPIRE_PROCESS":       "web",
					"EMPIRE_PROCESS_SCALE": "1",
					"SOURCE":               "acme-inc.web.v2",
				},
				Labels: map[string]string{
					"empire.app.process": "web",
				},
			},
		},
	}).Once().Return(nil)

	_, err = e.Set(context.Background(), empire.SetOpts{
		User: user,
		App:  app,
		Vars: empire.Vars{
			"RAILS_ENV": nil,
		},
	})
	assert.NoError(t, err)

	s.AssertExpectations(t)
}
Пример #6
0
func TestEmpire_Run_WithAllowCommandProcfile(t *testing.T) {
	e := empiretest.NewEmpire(t)
	e.AllowedCommands = empire.AllowCommandProcfile

	user := &empire.User{Name: "ejholmes"}

	app, err := e.Create(context.Background(), empire.CreateOpts{
		User: user,
		Name: "acme-inc",
	})
	assert.NoError(t, err)

	img := image.Image{Repository: "remind101/acme-inc"}
	_, err = e.Deploy(context.Background(), empire.DeployOpts{
		App:    app,
		User:   user,
		Output: empire.NewDeploymentStream(ioutil.Discard),
		Image:  img,
	})
	assert.NoError(t, err)

	s := new(mockScheduler)
	e.Scheduler = s

	err = e.Run(context.Background(), empire.RunOpts{
		User:    user,
		App:     app,
		Command: empire.MustParseCommand("bundle exec rake db:migrate"),

		// Detached Process
		Output: nil,
		Input:  nil,

		Env: map[string]string{
			"TERM": "xterm",
		},
	})
	assert.IsType(t, &empire.CommandNotAllowedError{}, err)

	s.On("Run", &scheduler.App{
		ID:      app.ID,
		Name:    "acme-inc",
		Release: "v1",
		Env: map[string]string{
			"EMPIRE_APPID":   app.ID,
			"EMPIRE_APPNAME": "acme-inc",
			"EMPIRE_RELEASE": "v1",
		},
		Labels: map[string]string{
			"empire.app.id":      app.ID,
			"empire.app.name":    "acme-inc",
			"empire.app.release": "v1",
		},
	},
		&scheduler.Process{
			Type:        "rake",
			Image:       img,
			Command:     []string{"bundle", "exec", "rake", "db:migrate"},
			Instances:   1,
			MemoryLimit: 536870912,
			CPUShares:   256,
			Nproc:       256,
			Env: map[string]string{
				"EMPIRE_PROCESS":       "rake",
				"EMPIRE_PROCESS_SCALE": "1",
				"SOURCE":               "acme-inc.rake.v1",
				"TERM":                 "xterm",
			},
			Labels: map[string]string{
				"empire.app.process": "rake",
				"empire.user":        "******",
			},
		}, nil, nil).Return(nil)

	err = e.Run(context.Background(), empire.RunOpts{
		User:    user,
		App:     app,
		Command: empire.MustParseCommand("rake db:migrate"),

		// Detached Process
		Output: nil,
		Input:  nil,

		Env: map[string]string{
			"TERM": "xterm",
		},
	})
	assert.NoError(t, err)

	s.AssertExpectations(t)
}
Пример #7
0
func TestEmpire_Deploy_Concurrent(t *testing.T) {
	e := empiretest.NewEmpire(t)
	s := new(mockScheduler)
	e.Scheduler = scheduler.NewFakeScheduler()
	e.ProcfileExtractor = empiretest.ExtractProcfile(procfile.ExtendedProcfile{
		"web": procfile.Process{
			Command: []string{"./bin/web"},
		},
	})

	user := &empire.User{Name: "ejholmes"}

	// Create the first release for this app.
	r, err := e.Deploy(context.Background(), empire.DeployOpts{
		User:   user,
		Output: empire.NewDeploymentStream(ioutil.Discard),
		Image:  image.Image{Repository: "remind101/acme-inc"},
	})
	assert.NoError(t, err)
	assert.Equal(t, 1, r.Version)

	// We'll use the procfile extractor to synchronize two concurrent
	// deployments.
	v2Started, v3Started := make(chan struct{}), make(chan struct{})
	e.ProcfileExtractor = empire.ProcfileExtractorFunc(func(ctx context.Context, img image.Image, w io.Writer) ([]byte, error) {
		switch img.Tag {
		case "v2":
			close(v2Started)
			<-v3Started
		case "v3":
			close(v3Started)
		}
		return procfile.Marshal(procfile.ExtendedProcfile{
			"web": procfile.Process{
				Command: []string{"./bin/web"},
			},
		})
	})

	v2Done := make(chan struct{})
	go func() {
		r, err := e.Deploy(context.Background(), empire.DeployOpts{
			User:   user,
			Output: empire.NewDeploymentStream(ioutil.Discard),
			Image:  image.Image{Repository: "remind101/acme-inc", Tag: "v2"},
		})
		assert.NoError(t, err)
		assert.Equal(t, 2, r.Version)
		close(v2Done)
	}()

	<-v2Started

	r, err = e.Deploy(context.Background(), empire.DeployOpts{
		User:   user,
		Output: empire.NewDeploymentStream(ioutil.Discard),
		Image:  image.Image{Repository: "remind101/acme-inc", Tag: "v3"},
	})
	assert.NoError(t, err)
	assert.Equal(t, 3, r.Version)

	<-v2Done

	s.AssertExpectations(t)
}