store: support multiple deployments per path

- Add support for multiple deployments per path in the Store
- Add a bunch of tests
- Change the Lookup deployment API
- Add disambiguation in the CLI commands

Fixes #231

Signed-off-by: Andrea Luzzardi <aluzzardi@gmail.com>
This commit is contained in:
Andrea Luzzardi 2021-04-02 16:38:27 -07:00
parent 540f373993
commit 33a7770459
3 changed files with 77 additions and 26 deletions

View File

@ -38,7 +38,26 @@ func GetCurrentDeploymentState(ctx context.Context, store *dagger.Store) *dagger
Str("deploymentPath", wd).
Msg("failed to lookup deployment by path")
}
return st
if len(st) == 0 {
lg.
Fatal().
Err(err).
Str("deploymentPath", wd).
Msg("no deployments match the current directory")
}
if len(st) > 1 {
deployments := []string{}
for _, s := range st {
deployments = append(deployments, s.Name)
}
lg.
Fatal().
Err(err).
Str("deploymentPath", wd).
Strs("deployments", deployments).
Msg("multiple deployments match the current directory, select one with `--deployment`")
}
return st[0]
}
// Re-compute a deployment (equivalent to `dagger up`).

View File

@ -30,8 +30,8 @@ type Store struct {
// Various indices for fast lookups
deploymentsByName map[string]*DeploymentState
deploymentsByPath map[string]*DeploymentState
pathsByDeployment map[string][]string
deploymentsByPath map[string][]*DeploymentState
pathsByDeploymentID map[string][]string
}
func NewStore(root string) (*Store, error) {
@ -39,8 +39,8 @@ func NewStore(root string) (*Store, error) {
root: root,
deployments: make(map[string]*DeploymentState),
deploymentsByName: make(map[string]*DeploymentState),
deploymentsByPath: make(map[string]*DeploymentState),
pathsByDeployment: make(map[string][]string),
deploymentsByPath: make(map[string][]*DeploymentState),
pathsByDeploymentID: make(map[string][]string),
}
return store, store.loadAll()
}
@ -120,8 +120,8 @@ func (s *Store) indexDeployment(r *DeploymentState) {
if i.Type != InputTypeDir {
return
}
s.deploymentsByPath[i.Dir.Path] = r
s.pathsByDeployment[r.ID] = append(s.pathsByDeployment[r.ID], i.Dir.Path)
s.deploymentsByPath[i.Dir.Path] = append(s.deploymentsByPath[i.Dir.Path], r)
s.pathsByDeploymentID[r.ID] = append(s.pathsByDeploymentID[r.ID], i.Dir.Path)
}
mapPath(r.PlanSource)
@ -138,10 +138,18 @@ func (s *Store) deindexDeployment(id string) {
delete(s.deployments, r.ID)
delete(s.deploymentsByName, r.Name)
for _, p := range s.pathsByDeployment[r.ID] {
delete(s.deploymentsByPath, p)
for _, p := range s.pathsByDeploymentID[r.ID] {
// Remove this deployments from the path->deployment mapping
deployments := []*DeploymentState{}
for _, d := range s.deploymentsByPath[p] {
if d.ID == r.ID {
continue
}
delete(s.pathsByDeployment, r.ID)
deployments = append(deployments, d)
}
s.deploymentsByPath[p] = deployments
}
delete(s.pathsByDeploymentID, r.ID)
}
func (s *Store) reindexDeployment(r *DeploymentState) {
@ -205,13 +213,13 @@ func (s *Store) LookupDeploymentByName(ctx context.Context, name string) (*Deplo
return st, nil
}
func (s *Store) LookupDeploymentByPath(ctx context.Context, path string) (*DeploymentState, error) {
func (s *Store) LookupDeploymentByPath(ctx context.Context, path string) ([]*DeploymentState, error) {
s.l.RLock()
defer s.l.RUnlock()
st, ok := s.deploymentsByPath[path]
if !ok {
return nil, fmt.Errorf("%s: %w", path, ErrDeploymentNotExist)
return []*DeploymentState{}, nil
}
return st, nil
}

View File

@ -66,34 +66,58 @@ func TestStoreLookupByPath(t *testing.T) {
require.NoError(t, store.CreateDeployment(ctx, st))
// Lookup by path
r, err := store.LookupDeploymentByPath(ctx, "/test/path")
deployments, err := store.LookupDeploymentByPath(ctx, "/test/path")
require.NoError(t, err)
require.NotNil(t, r)
require.Equal(t, st.ID, r.ID)
require.Len(t, deployments, 1)
require.Equal(t, st.ID, deployments[0].ID)
// Add a new path
require.NoError(t, st.AddInput("bar", DirInput("/test/anotherpath", []string{})))
require.NoError(t, store.UpdateDeployment(ctx, st, nil))
// Lookup by the previous path
r, err = store.LookupDeploymentByPath(ctx, "/test/path")
deployments, err = store.LookupDeploymentByPath(ctx, "/test/path")
require.NoError(t, err)
require.Equal(t, st.ID, r.ID)
require.Len(t, deployments, 1)
require.Equal(t, st.ID, deployments[0].ID)
// Lookup by the new path
r, err = store.LookupDeploymentByPath(ctx, "/test/anotherpath")
deployments, err = store.LookupDeploymentByPath(ctx, "/test/anotherpath")
require.NoError(t, err)
require.Equal(t, st.ID, r.ID)
require.Len(t, deployments, 1)
require.Equal(t, st.ID, deployments[0].ID)
// Remove a path
require.NoError(t, st.RemoveInputs("foo"))
require.NoError(t, store.UpdateDeployment(ctx, st, nil))
// Lookup by the removed path should fail
_, err = store.LookupDeploymentByPath(ctx, "/test/path")
require.Error(t, err)
deployments, err = store.LookupDeploymentByPath(ctx, "/test/path")
require.NoError(t, err)
require.Len(t, deployments, 0)
// Lookup by the other path should still work
_, err = store.LookupDeploymentByPath(ctx, "/test/anotherpath")
deployments, err = store.LookupDeploymentByPath(ctx, "/test/anotherpath")
require.NoError(t, err)
require.Len(t, deployments, 1)
// Add another deployment using the same path
otherSt := &DeploymentState{
Name: "test2",
}
require.NoError(t, otherSt.AddInput("foo", DirInput("/test/anotherpath", []string{})))
require.NoError(t, store.CreateDeployment(ctx, otherSt))
// Lookup by path should return both deployments
deployments, err = store.LookupDeploymentByPath(ctx, "/test/anotherpath")
require.NoError(t, err)
require.Len(t, deployments, 2)
// Remove the first deployment. Lookup by path should still return the
// second deployment.
require.NoError(t, store.DeleteDeployment(ctx, st, nil))
deployments, err = store.LookupDeploymentByPath(ctx, "/test/anotherpath")
require.NoError(t, err)
require.Len(t, deployments, 1)
require.Equal(t, otherSt.ID, deployments[0].ID)
}