terminology: rename route -> deployment

Signed-off-by: Andrea Luzzardi <aluzzardi@gmail.com>
This commit is contained in:
Andrea Luzzardi
2021-03-30 19:09:35 -07:00
committed by Solomon Hykes
parent e503e12cff
commit f901918266
22 changed files with 228 additions and 234 deletions

View File

@@ -61,7 +61,7 @@ func NewClient(ctx context.Context, host string) (*Client, error) {
}
// FIXME: return completed *Route, instead of *compiler.Value
func (c *Client) Up(ctx context.Context, route *Route) (*compiler.Value, error) {
func (c *Client) Up(ctx context.Context, deployment *Deployment) (*compiler.Value, error) {
lg := log.Ctx(ctx)
eg, gctx := errgroup.WithContext(ctx)
@@ -78,7 +78,7 @@ func (c *Client) Up(ctx context.Context, route *Route) (*compiler.Value, error)
outr, outw := io.Pipe()
eg.Go(func() error {
defer outw.Close()
return c.buildfn(gctx, route, events, outw)
return c.buildfn(gctx, deployment, events, outw)
})
// Spawn output retriever
@@ -95,11 +95,11 @@ func (c *Client) Up(ctx context.Context, route *Route) (*compiler.Value, error)
return out, compiler.Err(eg.Wait())
}
func (c *Client) buildfn(ctx context.Context, route *Route, ch chan *bk.SolveStatus, w io.WriteCloser) error {
func (c *Client) buildfn(ctx context.Context, deployment *Deployment, ch chan *bk.SolveStatus, w io.WriteCloser) error {
lg := log.Ctx(ctx)
// Scan local dirs to grant access
localdirs := route.LocalDirs()
localdirs := deployment.LocalDirs()
for label, dir := range localdirs {
abs, err := filepath.Abs(dir)
if err != nil {
@@ -132,24 +132,24 @@ func (c *Client) buildfn(ctx context.Context, route *Route, ch chan *bk.SolveSta
s := NewSolver(c.c, gw, ch)
lg.Debug().Msg("loading configuration")
if err := route.LoadLayout(ctx, s); err != nil {
if err := deployment.LoadLayout(ctx, s); err != nil {
return nil, err
}
// Compute output overlay
lg.Debug().Msg("computing route")
if err := route.Up(ctx, s, nil); err != nil {
lg.Debug().Msg("computing deployment")
if err := deployment.Up(ctx, s, nil); err != nil {
return nil, err
}
// Export route to a cue directory
// Export deployment to a cue directory
// FIXME: this should be elsewhere
lg.Debug().Msg("exporting route")
span, _ := opentracing.StartSpanFromContext(ctx, "Route.Export")
lg.Debug().Msg("exporting deployment")
span, _ := opentracing.StartSpanFromContext(ctx, "Deployment.Export")
defer span.Finish()
st := llb.Scratch().File(
llb.Mkfile("state.cue", 0600, route.State().JSON()),
llb.Mkfile("state.cue", 0600, deployment.State().JSON()),
llb.WithCustomName("[internal] serializing state to JSON"),
)
ref, err := s.Solve(ctx, st)
@@ -178,7 +178,7 @@ func (c *Client) buildfn(ctx context.Context, route *Route, ch chan *bk.SolveSta
func (c *Client) outputfn(ctx context.Context, r io.Reader) (*compiler.Value, error) {
lg := log.Ctx(ctx)
// FIXME: merge this into route output.
// FIXME: merge this into deployment output.
out := compiler.EmptyStruct()
tr := tar.NewReader(r)

View File

@@ -18,17 +18,17 @@ import (
"github.com/rs/zerolog/log"
)
// Contents of a route serialized to a file
type RouteState struct {
// Globally unique route ID
// Contents of a deployment serialized to a file
type DeploymentState struct {
// Globally unique deployment ID
ID string `json:"id,omitempty"`
// Human-friendly route name.
// A route may have more than one name.
// Human-friendly deployment name.
// A deployment may have more than one name.
// FIXME: store multiple names?
Name string `json:"name,omitempty"`
// Cue module containing the route layout
// Cue module containing the deployment layout
// The input's top-level artifact is used as a module directory.
LayoutSource Input `json:"layout,omitempty"`
@@ -40,34 +40,29 @@ type inputKV struct {
Value Input `json:"value,omitempty"`
}
func (r *RouteState) AddInput(key string, value Input) error {
r.Inputs = append(r.Inputs, inputKV{Key: key, Value: value})
func (s *DeploymentState) AddInput(key string, value Input) error {
s.Inputs = append(s.Inputs, inputKV{Key: key, Value: value})
return nil
}
// Remove all inputs at the given key, including sub-keys.
// For example RemoveInputs("foo.bar") will remove all inputs
// at foo.bar, foo.bar.baz, etc.
func (r *RouteState) RemoveInputs(key string) error {
newInputs := make([]inputKV, 0, len(r.Inputs))
for _, i := range r.Inputs {
func (s *DeploymentState) RemoveInputs(key string) error {
newInputs := make([]inputKV, 0, len(s.Inputs))
for _, i := range s.Inputs {
if i.Key == key {
continue
}
newInputs = append(newInputs, i)
}
r.Inputs = newInputs
s.Inputs = newInputs
return nil
}
type Route struct {
st *RouteState
// Env boot script, eg. `[{do:"local",dir:"."}]`
// FIXME: rename to 'update' (script to update the env config)
// FIXME: embed update script in base as '#update' ?
// FIXME: simplify Env by making it single layer? Each layer is one r.
type Deployment struct {
st *DeploymentState
// Layer 1: layout configuration
layout *compiler.Value
@@ -82,9 +77,9 @@ type Route struct {
state *compiler.Value
}
func NewRoute(st *RouteState) (*Route, error) {
func NewDeployment(st *DeploymentState) (*Deployment, error) {
empty := compiler.EmptyStruct()
r := &Route{
d := &Deployment{
st: st,
layout: empty,
input: empty,
@@ -98,55 +93,55 @@ func NewRoute(st *RouteState) (*Route, error) {
return nil, err
}
if input.Key == "" {
r.input, err = r.input.Merge(v)
d.input, err = d.input.Merge(v)
} else {
r.input, err = r.input.MergeTarget(v, input.Key)
d.input, err = d.input.MergeTarget(v, input.Key)
}
if err != nil {
return nil, err
}
}
if err := r.mergeState(); err != nil {
if err := d.mergeState(); err != nil {
return nil, err
}
return r, nil
return d, nil
}
func (r *Route) ID() string {
return r.st.ID
func (d *Deployment) ID() string {
return d.st.ID
}
func (r *Route) Name() string {
return r.st.Name
func (d *Deployment) Name() string {
return d.st.Name
}
func (r *Route) LayoutSource() Input {
return r.st.LayoutSource
func (d *Deployment) LayoutSource() Input {
return d.st.LayoutSource
}
func (r *Route) Layout() *compiler.Value {
return r.layout
func (d *Deployment) Layout() *compiler.Value {
return d.layout
}
func (r *Route) Input() *compiler.Value {
return r.input
func (d *Deployment) Input() *compiler.Value {
return d.input
}
func (r *Route) Output() *compiler.Value {
return r.output
func (d *Deployment) Output() *compiler.Value {
return d.output
}
func (r *Route) State() *compiler.Value {
return r.state
func (d *Deployment) State() *compiler.Value {
return d.state
}
// LoadLayout loads the layout
func (r *Route) LoadLayout(ctx context.Context, s Solver) error {
span, ctx := opentracing.StartSpanFromContext(ctx, "route.Update")
func (d *Deployment) LoadLayout(ctx context.Context, s Solver) error {
span, ctx := opentracing.StartSpanFromContext(ctx, "deployment.LoadLayout")
defer span.Finish()
layoutSource, err := r.st.LayoutSource.Compile()
layoutSource, err := d.st.LayoutSource.Compile()
if err != nil {
return err
}
@@ -166,17 +161,17 @@ func (r *Route) LoadLayout(ctx context.Context, s Solver) error {
if err != nil {
return fmt.Errorf("layout config: %w", err)
}
r.layout = layout
d.layout = layout
// Commit
return r.mergeState()
return d.mergeState()
}
// Scan all scripts in the environment for references to local directories (do:"local"),
// Scan all scripts in the deployment for references to local directories (do:"local"),
// and return all referenced directory names.
// This is used by clients to grant access to local directories when they are referenced
// by user-specified scripts.
func (r *Route) LocalDirs() map[string]string {
func (d *Deployment) LocalDirs() map[string]string {
dirs := map[string]string{}
localdirs := func(code ...*compiler.Value) {
Analyze(
@@ -185,7 +180,6 @@ func (r *Route) LocalDirs() map[string]string {
if err != nil {
return err
}
// FIXME: merge Env into Route, or fix the linter error
if do != "local" {
return nil
}
@@ -199,9 +193,9 @@ func (r *Route) LocalDirs() map[string]string {
code...,
)
}
// 1. Scan the environment state
// 1. Scan the deployment state
// FIXME: use a common `flow` instance to avoid rescanning the tree.
inst := r.state.CueInst()
inst := d.state.CueInst()
flow := cueflow.New(&cueflow.Config{}, inst, newTaskFunc(inst, noOpRunner))
for _, t := range flow.Tasks() {
v := compiler.Wrap(t.Value(), inst)
@@ -209,7 +203,7 @@ func (r *Route) LocalDirs() map[string]string {
}
// 2. Scan the layout
layout, err := r.st.LayoutSource.Compile()
layout, err := d.st.LayoutSource.Compile()
if err != nil {
panic(err)
}
@@ -218,7 +212,7 @@ func (r *Route) LocalDirs() map[string]string {
}
// FIXME: this is just a 3-way merge. Add var args to compiler.Value.Merge.
func (r *Route) mergeState() error {
func (d *Deployment) mergeState() error {
// FIXME: make this cleaner in *compiler.Value by keeping intermediary instances
// FIXME: state.CueInst() must return an instance with the same
// contents as state.v, for the purposes of cueflow.
@@ -231,15 +225,15 @@ func (r *Route) mergeState() error {
err error
)
stateInst, err = stateInst.Fill(r.layout.Cue())
stateInst, err = stateInst.Fill(d.layout.Cue())
if err != nil {
return fmt.Errorf("merge base & input: %w", err)
}
stateInst, err = stateInst.Fill(r.input.Cue())
stateInst, err = stateInst.Fill(d.input.Cue())
if err != nil {
return fmt.Errorf("merge base & input: %w", err)
}
stateInst, err = stateInst.Fill(r.output.Cue())
stateInst, err = stateInst.Fill(d.output.Cue())
if err != nil {
return fmt.Errorf("merge output with base & input: %w", err)
}
@@ -247,24 +241,24 @@ func (r *Route) mergeState() error {
state = compiler.Wrap(stateInst.Value(), stateInst)
// commit
r.state = state
d.state = state
return nil
}
type UpOpts struct{}
// Up missing values in env configuration, and write them to state.
func (r *Route) Up(ctx context.Context, s Solver, _ *UpOpts) error {
// Up missing values in deployment configuration, and write them to state.
func (d *Deployment) Up(ctx context.Context, s Solver, _ *UpOpts) error {
span, ctx := opentracing.StartSpanFromContext(ctx, "r.Compute")
defer span.Finish()
lg := log.Ctx(ctx)
// Cueflow cue instance
inst := r.state.CueInst()
inst := d.state.CueInst()
// Reset the output
r.output = compiler.EmptyStruct()
d.output = compiler.EmptyStruct()
// Cueflow config
flowCfg := &cueflow.Config{
@@ -284,7 +278,7 @@ func (r *Route) Up(ctx context.Context, s Solver, _ *UpOpts) error {
}
// Merge task value into output
var err error
r.output, err = r.output.MergePath(t.Value(), t.Path())
d.output, err = d.output.MergePath(t.Value(), t.Path())
if err != nil {
lg.
Error().
@@ -305,17 +299,17 @@ func (r *Route) Up(ctx context.Context, s Solver, _ *UpOpts) error {
span, _ := opentracing.StartSpanFromContext(ctx, "merge state")
defer span.Finish()
return r.mergeState()
return d.mergeState()
}
}
type DownOpts struct{}
func (r *Route) Down(ctx context.Context, _ *DownOpts) error {
func (d *Deployment) Down(ctx context.Context, _ *DownOpts) error {
panic("NOT IMPLEMENTED")
}
func (r *Route) Query(ctx context.Context, expr interface{}, o *QueryOpts) (*compiler.Value, error) {
func (d *Deployment) Query(ctx context.Context, expr interface{}, o *QueryOpts) (*compiler.Value, error) {
panic("NOT IMPLEMENTED")
}

View File

@@ -7,15 +7,15 @@ import (
)
func TestInputDir(t *testing.T) {
st := &RouteState{
st := &DeploymentState{
LayoutSource: DirInput("/tmp/source", []string{}),
}
require.NoError(t, st.AddInput("www.source", DirInput(".", []string{})))
route, err := NewRoute(st)
deployment, err := NewDeployment(st)
require.NoError(t, err)
localdirs := route.LocalDirs()
localdirs := deployment.LocalDirs()
require.Len(t, localdirs, 2)
require.Contains(t, localdirs, ".")
require.Contains(t, localdirs, "/tmp/source")

View File

@@ -13,12 +13,12 @@ import (
)
var (
ErrRouteExist = errors.New("route already exists")
ErrRouteNotExist = errors.New("route doesn't exist")
ErrDeploymentExist = errors.New("deployment already exists")
ErrDeploymentNotExist = errors.New("deployment doesn't exist")
)
const (
defaultStoreRoot = "$HOME/.config/dagger/routes"
defaultStoreRoot = "$HOME/.config/dagger/deployments"
)
type Store struct {
@@ -26,21 +26,21 @@ type Store struct {
l sync.RWMutex
routes map[string]*RouteState
deployments map[string]*DeploymentState
// Various indices for fast lookups
routesByName map[string]*RouteState
routesByPath map[string]*RouteState
pathsByRoute map[string][]string
deploymentsByName map[string]*DeploymentState
deploymentsByPath map[string]*DeploymentState
pathsByDeployment map[string][]string
}
func NewStore(root string) (*Store, error) {
store := &Store{
root: root,
routes: make(map[string]*RouteState),
routesByName: make(map[string]*RouteState),
routesByPath: make(map[string]*RouteState),
pathsByRoute: make(map[string][]string),
root: root,
deployments: make(map[string]*DeploymentState),
deploymentsByName: make(map[string]*DeploymentState),
deploymentsByPath: make(map[string]*DeploymentState),
pathsByDeployment: make(map[string][]string),
}
return store, store.loadAll()
}
@@ -49,8 +49,8 @@ func DefaultStore() (*Store, error) {
return NewStore(os.ExpandEnv(defaultStoreRoot))
}
func (s *Store) routePath(name string) string {
return path.Join(s.root, name, "route.json")
func (s *Store) deploymentPath(name string) string {
return path.Join(s.root, name, "deployment.json")
}
func (s *Store) loadAll() error {
@@ -66,7 +66,7 @@ func (s *Store) loadAll() error {
if !f.IsDir() {
continue
}
if err := s.loadRoute(f.Name()); err != nil {
if err := s.loadDeployment(f.Name()); err != nil {
return err
}
}
@@ -74,21 +74,21 @@ func (s *Store) loadAll() error {
return nil
}
func (s *Store) loadRoute(name string) error {
data, err := os.ReadFile(s.routePath(name))
func (s *Store) loadDeployment(name string) error {
data, err := os.ReadFile(s.deploymentPath(name))
if err != nil {
return err
}
var st RouteState
var st DeploymentState
if err := json.Unmarshal(data, &st); err != nil {
return err
}
s.indexRoute(&st)
s.indexDeployment(&st)
return nil
}
func (s *Store) syncRoute(r *RouteState) error {
p := s.routePath(r.Name)
func (s *Store) syncDeployment(r *DeploymentState) error {
p := s.deploymentPath(r.Name)
if err := os.MkdirAll(path.Dir(p), 0755); err != nil {
return err
@@ -103,21 +103,21 @@ func (s *Store) syncRoute(r *RouteState) error {
return err
}
s.reindexRoute(r)
s.reindexDeployment(r)
return nil
}
func (s *Store) indexRoute(r *RouteState) {
s.routes[r.ID] = r
s.routesByName[r.Name] = r
func (s *Store) indexDeployment(r *DeploymentState) {
s.deployments[r.ID] = r
s.deploymentsByName[r.Name] = r
mapPath := func(i Input) {
if i.Type != InputTypeDir {
return
}
s.routesByPath[i.Dir.Path] = r
s.pathsByRoute[r.ID] = append(s.pathsByRoute[r.ID], i.Dir.Path)
s.deploymentsByPath[i.Dir.Path] = r
s.pathsByDeployment[r.ID] = append(s.pathsByDeployment[r.ID], i.Dir.Path)
}
mapPath(r.LayoutSource)
@@ -126,101 +126,101 @@ func (s *Store) indexRoute(r *RouteState) {
}
}
func (s *Store) deindexRoute(id string) {
r, ok := s.routes[id]
func (s *Store) deindexDeployment(id string) {
r, ok := s.deployments[id]
if !ok {
return
}
delete(s.routes, r.ID)
delete(s.routesByName, r.Name)
delete(s.deployments, r.ID)
delete(s.deploymentsByName, r.Name)
for _, p := range s.pathsByRoute[r.ID] {
delete(s.routesByPath, p)
for _, p := range s.pathsByDeployment[r.ID] {
delete(s.deploymentsByPath, p)
}
delete(s.pathsByRoute, r.ID)
delete(s.pathsByDeployment, r.ID)
}
func (s *Store) reindexRoute(r *RouteState) {
s.deindexRoute(r.ID)
s.indexRoute(r)
func (s *Store) reindexDeployment(r *DeploymentState) {
s.deindexDeployment(r.ID)
s.indexDeployment(r)
}
func (s *Store) CreateRoute(ctx context.Context, st *RouteState) error {
func (s *Store) CreateDeployment(ctx context.Context, st *DeploymentState) error {
s.l.Lock()
defer s.l.Unlock()
if _, ok := s.routesByName[st.Name]; ok {
return fmt.Errorf("%s: %w", st.Name, ErrRouteExist)
if _, ok := s.deploymentsByName[st.Name]; ok {
return fmt.Errorf("%s: %w", st.Name, ErrDeploymentExist)
}
st.ID = uuid.New().String()
return s.syncRoute(st)
return s.syncDeployment(st)
}
type UpdateOpts struct{}
func (s *Store) UpdateRoute(ctx context.Context, r *RouteState, o *UpdateOpts) error {
func (s *Store) UpdateDeployment(ctx context.Context, r *DeploymentState, o *UpdateOpts) error {
s.l.Lock()
defer s.l.Unlock()
return s.syncRoute(r)
return s.syncDeployment(r)
}
type DeleteOpts struct{}
func (s *Store) DeleteRoute(ctx context.Context, r *RouteState, o *DeleteOpts) error {
func (s *Store) DeleteDeployment(ctx context.Context, r *DeploymentState, o *DeleteOpts) error {
s.l.Lock()
defer s.l.Unlock()
if err := os.Remove(s.routePath(r.Name)); err != nil {
if err := os.Remove(s.deploymentPath(r.Name)); err != nil {
return err
}
s.deindexRoute(r.ID)
s.deindexDeployment(r.ID)
return nil
}
func (s *Store) LookupRouteByID(ctx context.Context, id string) (*RouteState, error) {
func (s *Store) LookupDeploymentByID(ctx context.Context, id string) (*DeploymentState, error) {
s.l.RLock()
defer s.l.RUnlock()
st, ok := s.routes[id]
st, ok := s.deployments[id]
if !ok {
return nil, fmt.Errorf("%s: %w", id, ErrRouteNotExist)
return nil, fmt.Errorf("%s: %w", id, ErrDeploymentNotExist)
}
return st, nil
}
func (s *Store) LookupRouteByName(ctx context.Context, name string) (*RouteState, error) {
func (s *Store) LookupDeploymentByName(ctx context.Context, name string) (*DeploymentState, error) {
s.l.RLock()
defer s.l.RUnlock()
st, ok := s.routesByName[name]
st, ok := s.deploymentsByName[name]
if !ok {
return nil, fmt.Errorf("%s: %w", name, ErrRouteNotExist)
return nil, fmt.Errorf("%s: %w", name, ErrDeploymentNotExist)
}
return st, nil
}
func (s *Store) LookupRouteByPath(ctx context.Context, path string) (*RouteState, error) {
func (s *Store) LookupDeploymentByPath(ctx context.Context, path string) (*DeploymentState, error) {
s.l.RLock()
defer s.l.RUnlock()
st, ok := s.routesByPath[path]
st, ok := s.deploymentsByPath[path]
if !ok {
return nil, fmt.Errorf("%s: %w", path, ErrRouteNotExist)
return nil, fmt.Errorf("%s: %w", path, ErrDeploymentNotExist)
}
return st, nil
}
func (s *Store) ListRoutes(ctx context.Context) ([]*RouteState, error) {
func (s *Store) ListDeployments(ctx context.Context) ([]*DeploymentState, error) {
s.l.RLock()
defer s.l.RUnlock()
routes := make([]*RouteState, 0, len(s.routes))
deployments := make([]*DeploymentState, 0, len(s.deployments))
for _, st := range s.routes {
routes = append(routes, st)
for _, st := range s.deployments {
deployments = append(deployments, st)
}
return routes, nil
return deployments, nil
}

View File

@@ -17,38 +17,38 @@ func TestStoreLoad(t *testing.T) {
store, err := NewStore(root)
require.NoError(t, err)
_, err = store.LookupRouteByName(ctx, "notexist")
_, err = store.LookupDeploymentByName(ctx, "notexist")
require.Error(t, err)
require.True(t, errors.Is(err, ErrRouteNotExist))
require.True(t, errors.Is(err, ErrDeploymentNotExist))
st := &RouteState{
st := &DeploymentState{
Name: "test",
}
require.NoError(t, store.CreateRoute(ctx, st))
require.NoError(t, store.CreateDeployment(ctx, st))
checkRoutes := func(store *Store) {
r, err := store.LookupRouteByID(ctx, st.ID)
checkDeployments := func(store *Store) {
r, err := store.LookupDeploymentByID(ctx, st.ID)
require.NoError(t, err)
require.NotNil(t, r)
require.Equal(t, "test", r.Name)
r, err = store.LookupRouteByName(ctx, "test")
r, err = store.LookupDeploymentByName(ctx, "test")
require.NoError(t, err)
require.NotNil(t, r)
require.Equal(t, "test", r.Name)
routes, err := store.ListRoutes(ctx)
deployments, err := store.ListDeployments(ctx)
require.NoError(t, err)
require.Len(t, routes, 1)
require.Equal(t, "test", routes[0].Name)
require.Len(t, deployments, 1)
require.Equal(t, "test", deployments[0].Name)
}
checkRoutes(store)
checkDeployments(store)
// Reload the routes from disk and check again
// Reload the deployments from disk and check again
newStore, err := NewStore(root)
require.NoError(t, err)
checkRoutes(newStore)
checkDeployments(newStore)
}
func TestStoreLookupByPath(t *testing.T) {
@@ -59,41 +59,41 @@ func TestStoreLookupByPath(t *testing.T) {
store, err := NewStore(root)
require.NoError(t, err)
st := &RouteState{
st := &DeploymentState{
Name: "test",
}
require.NoError(t, st.AddInput("foo", DirInput("/test/path", []string{})))
require.NoError(t, store.CreateRoute(ctx, st))
require.NoError(t, store.CreateDeployment(ctx, st))
// Lookup by path
r, err := store.LookupRouteByPath(ctx, "/test/path")
r, err := store.LookupDeploymentByPath(ctx, "/test/path")
require.NoError(t, err)
require.NotNil(t, r)
require.Equal(t, st.ID, r.ID)
// Add a new path
require.NoError(t, st.AddInput("bar", DirInput("/test/anotherpath", []string{})))
require.NoError(t, store.UpdateRoute(ctx, st, nil))
require.NoError(t, store.UpdateDeployment(ctx, st, nil))
// Lookup by the previous path
r, err = store.LookupRouteByPath(ctx, "/test/path")
r, err = store.LookupDeploymentByPath(ctx, "/test/path")
require.NoError(t, err)
require.Equal(t, st.ID, r.ID)
// Lookup by the new path
r, err = store.LookupRouteByPath(ctx, "/test/anotherpath")
r, err = store.LookupDeploymentByPath(ctx, "/test/anotherpath")
require.NoError(t, err)
require.Equal(t, st.ID, r.ID)
// Remove a path
require.NoError(t, st.RemoveInputs("foo"))
require.NoError(t, store.UpdateRoute(ctx, st, nil))
require.NoError(t, store.UpdateDeployment(ctx, st, nil))
// Lookup by the removed path should fail
_, err = store.LookupRouteByPath(ctx, "/test/path")
_, err = store.LookupDeploymentByPath(ctx, "/test/path")
require.Error(t, err)
// Lookup by the other path should still work
_, err = store.LookupRouteByPath(ctx, "/test/anotherpath")
_, err = store.LookupDeploymentByPath(ctx, "/test/anotherpath")
require.NoError(t, err)
}