Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

deploy: container history #578

Merged
merged 22 commits into from
Mar 31, 2019
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
ed9496e
updated function description to match present functionality
seifghazi Feb 9, 2019
25ff9c6
added container bucket, successfully stores commit and date
seifghazi Feb 15, 2019
1dad3c7
database updates with container metadata on project deployment
seifghazi Feb 22, 2019
52098af
inline comment for clarity
seifghazi Feb 22, 2019
678635f
commented out test
seifghazi Feb 22, 2019
ce07f2c
Merge branch 'master' into deploy/293-quick-rollback
seifghazi Feb 24, 2019
d39d72a
remove logger
seifghazi Feb 26, 2019
54ecbf3
fixed conflicts
seifghazi Feb 26, 2019
243cb0c
merge branch
seifghazi Feb 26, 2019
e8b1af5
update logger
seifghazi Feb 26, 2019
1ea1d40
added error checks, metadata struct to store deployment data
seifghazi Mar 6, 2019
b91c1d3
add nested bucket functionality
seifghazi Mar 6, 2019
d0be09c
minor refactor, added nested buckets for each project
seifghazi Mar 6, 2019
8109bac
added tests covering bkt functionality
seifghazi Mar 29, 2019
9088cf1
updated insert query to resolve deadlock issue
seifghazi Mar 29, 2019
a80a910
Merge branch 'master' into deploy/293-quick-rollback
seifghazi Mar 29, 2019
2f5234f
more context with errors, added some error checks with bkt creation, …
seifghazi Mar 30, 2019
bcf4d3d
split function signature onto two lines
seifghazi Mar 30, 2019
15f71e6
more context to erros
seifghazi Mar 30, 2019
29acfbb
added counterfeiter
seifghazi Mar 30, 2019
758d804
added some comments for future reference
seifghazi Mar 30, 2019
6008ccd
reorder comment to fix lint error
seifghazi Mar 31, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions daemon/inertiad/build/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,8 +280,7 @@ func (b *Builder) dockerBuild(d Config, cli *docker.Client,
return func() error { return b.run(ctx, cli, d.Name, containerResp.ID, out) }, nil
}

// run starts project and tracks all active project containers and pipes an error
// to the returned channel if any container exits or errors.
// run starts project and tracks all active project containers
func (b *Builder) run(ctx context.Context, client *docker.Client, name, id string, out io.Writer) error {
reportProjectStartup(name, out)
return client.ContainerStart(ctx, id, types.ContainerStartOptions{})
Expand Down
7 changes: 7 additions & 0 deletions daemon/inertiad/daemon/up.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,5 +97,12 @@ func (s *Server) upHandler(w http.ResponseWriter, r *http.Request) {
return
}

// Update container management history following a successful build and deployment
err := s.deployment.UpdateContainerHistory(s.docker)
if err != nil {
stream.Error(res.ErrInternalServer("failed to update container history following build", err))
}

stream.Success(res.Msg("Project startup initiated!", http.StatusCreated))

seifghazi marked this conversation as resolved.
Show resolved Hide resolved
}
51 changes: 48 additions & 3 deletions daemon/inertiad/project/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@ import (
"fmt"
"io/ioutil"
"os"
"time"

"github.com/ubclaunchpad/inertia/daemon/inertiad/crypto"
bolt "go.etcd.io/bbolt"
)

var (
// database buckets
envVariableBucket = []byte("envVariables")
envVariableBucket = []byte("envVariables")
deployedProjectsBucket = []byte("deployedProjects")
)

// DeploymentDataManager stores persistent deployment configuration
Expand Down Expand Up @@ -50,6 +52,7 @@ func NewDataManager(dbPath string, keyPath string) (*DeploymentDataManager, erro
}
if err = db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucketIfNotExists(envVariableBucket)
_, err = tx.CreateBucketIfNotExists(deployedProjectsBucket)
seifghazi marked this conversation as resolved.
Show resolved Hide resolved
return err
}); err != nil {
return nil, fmt.Errorf("failed to instantiate database: %s", err.Error())
Expand All @@ -63,8 +66,7 @@ func NewDataManager(dbPath string, keyPath string) (*DeploymentDataManager, erro

// AddEnvVariable adds a new environment variable that will be applied
// to all project containers
func (c *DeploymentDataManager) AddEnvVariable(name, value string,
encrypt bool) error {
func (c *DeploymentDataManager) AddEnvVariable(name, value string, encrypt bool) error {
if len(name) == 0 || len(value) == 0 {
return errors.New("invalid env configuration")
}
Expand Down Expand Up @@ -138,6 +140,49 @@ func (c *DeploymentDataManager) GetEnvVariables(decrypt bool) ([]string, error)
return envs, err
}

// AddProjectBuildData stores and tracks metadata from successful builds
// TODO: definitely need to add more context to errors
seifghazi marked this conversation as resolved.
Show resolved Hide resolved
func (c *DeploymentDataManager) AddProjectBuildData(projectName string, mdata DeploymentMetadata) error {
bobheadxi marked this conversation as resolved.
Show resolved Hide resolved
// encode metadata so it can be stored as byte array
encodedMdata, err := json.Marshal(mdata)
if err != nil {
return err
}
return c.db.Update(func(tx *bolt.Tx) error {
deployedProjectsBucket := tx.Bucket(deployedProjectsBucket)
// if bkt with project name doesnt exist create new bkt, otherwise update existing bucket
if projectBkt := deployedProjectsBucket.Bucket([]byte(projectName)); projectBkt == nil {
projectBkt, err := deployedProjectsBucket.CreateBucket([]byte(projectName))
if err != nil {
return err
}

if err := projectBkt.Put([]byte(time.Now().String()), encodedMdata); err != nil {
return err
}
} else {
if err := c.UpdateProjectBuildData(projectName, encodedMdata); err != nil {
return err
bobheadxi marked this conversation as resolved.
Show resolved Hide resolved
}
}
return nil
})
}

// UpdateProjectBuildData updates existing project bkt with recent build's metadata
func (c *DeploymentDataManager) UpdateProjectBuildData(projectName string, mdata []byte) error {
seifghazi marked this conversation as resolved.
Show resolved Hide resolved
return c.db.Update(func(tx *bolt.Tx) error {
deployedProjectsBucket := tx.Bucket(deployedProjectsBucket)
projectBkt := deployedProjectsBucket.Bucket([]byte(projectName))

if err := projectBkt.Put([]byte(time.Now().String()), mdata); err != nil {
return err
}

return nil
})
}

func (c *DeploymentDataManager) destroy() error {
return c.db.Update(func(tx *bolt.Tx) error {
if err := tx.DeleteBucket(envVariableBucket); err != nil {
Expand Down
31 changes: 31 additions & 0 deletions daemon/inertiad/project/data_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,37 @@ func TestDataManager_EnvVariableOperations(t *testing.T) {
}
}

func TestDataManager_ProjectBuildDataOperations(t *testing.T) {
type args struct {
projectName string
metadata DeploymentMetadata
}
tests := []struct {
name string
args args
wantErr bool
}{
{"valid project build", args{"projectA", DeploymentMetadata{"hash", "ID", "status", "time"}}, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
dir := "./test_config"
err := os.Mkdir(dir, os.ModePerm)
assert.Nil(t, err)
defer os.RemoveAll(dir)

// Instantiate
c, err := NewDataManager(path.Join(dir, "deployment.db"), path.Join(dir, "key"))
assert.Nil(t, err)

// Add
err = c.AddProjectBuildData(tt.args.projectName, tt.args.metadata)
assert.Equal(t, tt.wantErr, (err != nil))
bobheadxi marked this conversation as resolved.
Show resolved Hide resolved

})
}
}

func TestDataManager_destroy(t *testing.T) {
dir := "./test_config"
err := os.Mkdir(dir, os.ModePerm)
Expand Down
61 changes: 61 additions & 0 deletions daemon/inertiad/project/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ type Deployer interface {
GetBranch() string
CompareRemotes(string) error

UpdateContainerHistory(cli *docker.Client) error

GetDataManager() (*DeploymentDataManager, bool)

Watch(*docker.Client) (<-chan string, <-chan error)
Expand Down Expand Up @@ -70,6 +72,15 @@ type DeploymentConfig struct {
PemFilePath string
}

// DeploymentMetadata is used to store metadata relevant
// to the most recent deployment
type DeploymentMetadata struct {
Hash string
ContainerID string
ContainerStatus string
StartedAt string
}

// NewDeployment creates a new deployment
func NewDeployment(
projectDirectory string,
Expand Down Expand Up @@ -314,6 +325,56 @@ func (d *Deployment) CompareRemotes(remoteURL string) error {
return nil
}

// UpdateContainerHistory will update container bucket with recent build's
// metadata
func (d *Deployment) UpdateContainerHistory(cli *docker.Client) error {

// Get project hash
head, err := d.repo.Head()
if err != nil {
return err
}
// Retrieve container for recently deployed project
ctx := context.Background()
var recentlyBuiltContainer types.Container
containers, err := cli.ContainerList(ctx, types.ContainerListOptions{})
if err != nil {
return err
}
for _, container := range containers {
if container.Names[0] == d.project {
bobheadxi marked this conversation as resolved.
Show resolved Hide resolved
recentlyBuiltContainer = container
}
}

// Get container metadata
var containerID string
if len(recentlyBuiltContainer.ID) > 0 {
containerID = recentlyBuiltContainer.ID
}
containerJSON, err := cli.ContainerInspect(ctx, containerID)
if err != nil {
return err // TODO: Add more context to error
}
containerState := containerJSON.ContainerJSONBase.State // similar to running "docker inspect {container}"
var containerStatus, containerStartedAtTime string
if containerState != nil {
containerStatus = containerState.Status
containerStartedAtTime = containerState.StartedAt
}

metadata := DeploymentMetadata{
Hash: head.Hash().String(),
ContainerID: containerID,
ContainerStatus: containerStatus,
StartedAt: containerStartedAtTime}

// Update db with newly built container metadata
d.dataManager.AddProjectBuildData(d.project, metadata)

return err
seifghazi marked this conversation as resolved.
Show resolved Hide resolved
}

// GetDataManager returns the class managing deployment data
func (d *Deployment) GetDataManager() (manager *DeploymentDataManager, found bool) {
if d.dataManager == nil {
Expand Down
Empty file added make
Empty file.
Empty file modified test/keys/id_rsa
100755 → 100644
Empty file.