Skip to content

Commit

Permalink
Add ability to move container from one namespace to another (#174)
Browse files Browse the repository at this point in the history
Related #130

Signed-off-by: Igor Shishkin <me@teran.dev>
  • Loading branch information
teran committed Aug 22, 2024
1 parent a27e830 commit 958fa22
Show file tree
Hide file tree
Showing 19 changed files with 164 additions and 27 deletions.
44 changes: 34 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ To do so archived relies on two storages: metadata and CAS.

Metadata is a some kind of database to store all of the things:

* namespaces - group of containers
* containers - some kind of directories
* versions - immutable version of the data in container
* objects - named data BLOBs with some additional metadata
Expand All @@ -49,7 +50,8 @@ archived is built with microservice architecture containing the following
components:

* archived-publisher - HTTP server to allow data listing and fetching
* archived-manager - gRPC API to manage containers, versions and objects
* archived-manager - gRPC API to manage namespaces, containers, versions and
objects
* archived-exporter - Prometheus metrics exporter for metadata entities
* CLI - CLI application to interact with manage component
* migrator - metadata migration tool
Expand Down Expand Up @@ -86,8 +88,8 @@ reference.
## CLI

archived-cli provides an CLI interface to operate archived including creating
containers, versions and objects. It works with archived-manager to handle
requests.
namespaces, containers, versions and objects. It works with archived-manager
to handle requests.

```shell
usage: archived-cli --endpoint=ENDPOINT [<flags>] <command> [<args> ...]
Expand All @@ -96,23 +98,42 @@ CLI interface for archived


Flags:
--[no-]help Show context-sensitive help (also try --help-long and --help-man).
-d, --[no-]debug Enable debug mode ($ARCHIVED_CLI_DEBUG)
-t, --[no-]trace Enable trace mode (debug mode on steroids) ($ARCHIVED_CLI_TRACE)
-s, --endpoint=ENDPOINT Manage API endpoint address ($ARCHIVED_CLI_ENDPOINT)
--[no-]insecure Do not use TLS for gRPC connection
--[no-]help Show context-sensitive help (also try --help-long and --help-man).
-d, --[no-]debug Enable debug mode ($ARCHIVED_CLI_DEBUG)
-t, --[no-]trace Enable trace mode (debug mode on steroids) ($ARCHIVED_CLI_TRACE)
-s, --endpoint=ENDPOINT Manager API endpoint address ($ARCHIVED_CLI_ENDPOINT)
--[no-]insecure Do not use TLS for gRPC connection
--[no-]insecure-skip-verify
Do not perform TLS certificate verification for gRPC connection
Do not perform TLS certificate verification for gRPC connection
--cache-dir="~/.cache/archived/cli/objects"
cache directory for objects
Stat-cache directory for objects ($ARCHIVED_CLI_STAT_CACHE_DIR)
-n, --namespace="default" namespace for containers to operate on

Commands:
help [<command>...]
Show help.

namespace create <name>
create new namespace

namespace rename <old-name> <new-name>
rename the given namespace

namespace delete <name>
delete the given namespace

namespace list
list namespaces

container create <name>
create new container

container move <name> <namespace>
move container to another namespace

container rename <old-name> <new-name>
rename the given container

container delete <name>
delete the given container

Expand Down Expand Up @@ -142,6 +163,9 @@ object url <container> <version> <key>

object delete <container> <version> <key>
delete object

stat-cache show-path
print actual cache path
```

## How build the project manually
Expand Down
5 changes: 5 additions & 0 deletions cli/service/pb_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ func (m *protoClientMock) CreateContainer(ctx context.Context, in *v1proto.Creat
return &v1proto.CreateContainerResponse{}, args.Error(0)
}

func (m *protoClientMock) MoveContainer(ctx context.Context, in *v1proto.MoveContainerRequest, opts ...grpc.CallOption) (*v1proto.MoveContainerResponse, error) {
args := m.Called(in.GetNamespace(), in.GetContainerName(), in.GetDestinationNamespace())
return &v1proto.MoveContainerResponse{}, args.Error(0)
}

func (m *protoClientMock) RenameContainer(ctx context.Context, in *v1proto.RenameContainerRequest, opts ...grpc.CallOption) (*v1proto.RenameContainerResponse, error) {
args := m.Called(in.GetNamespace(), in.GetOldName(), in.GetNewName())
return &v1proto.RenameContainerResponse{}, args.Error(0)
Expand Down
18 changes: 17 additions & 1 deletion cli/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type Service interface {
DeleteNamespace(namespaceName string) func(ctx context.Context) error

CreateContainer(namespaceName, containerName string) func(ctx context.Context) error
MoveContainer(namespaceName, containerName, destinationNamespace string) func(ctx context.Context) error
RenameContainer(namespaceName, oldName, newName string) func(ctx context.Context) error
ListContainers(namespaceName string) func(ctx context.Context) error
DeleteContainer(namespaceName, containerName string) func(ctx context.Context) error
Expand Down Expand Up @@ -129,6 +130,21 @@ func (s *service) CreateContainer(namespaceName, containerName string) func(ctx
}
}

func (s *service) MoveContainer(namespaceName, containerName, destinationNamespace string) func(ctx context.Context) error {
return func(ctx context.Context) error {
_, err := s.cli.MoveContainer(ctx, &v1proto.MoveContainerRequest{
Namespace: namespaceName,
ContainerName: containerName,
DestinationNamespace: destinationNamespace,
})
if err != nil {
return errors.Wrap(err, "error moving container")
}
fmt.Printf("container `%s` just moved from `%s` to `%s`\n", containerName, namespaceName, destinationNamespace)
return nil
}
}

func (s *service) RenameContainer(namespaceName, oldName, newName string) func(ctx context.Context) error {
return func(ctx context.Context) error {
_, err := s.cli.RenameContainer(ctx, &v1proto.RenameContainerRequest{
Expand Down Expand Up @@ -340,7 +356,7 @@ func (s *service) createVersionFromYUMRepository(ctx context.Context, namespaceN

fp, err := lb.Reader(ctx)
if err != nil {
return errors.Wrap(err, "error opening package file")
return errors.Wrap(err, "error getting reader for package file")
}

return uploadBlob(ctx, uploadURL, fp, size)
Expand Down
7 changes: 7 additions & 0 deletions cli/service/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ func (s *serviceTestSuite) TestCreateContainer() {
s.Require().NoError(fn(s.ctx))
}

func (s *serviceTestSuite) TestMoveContainer() {
s.cliMock.On("MoveContainer", defaultNamespace, "test-container", "new-namespace").Return(nil).Once()

fn := s.svc.MoveContainer(defaultNamespace, "test-container", "new-namespace")
s.Require().NoError(fn(s.ctx))
}

func (s *serviceTestSuite) TestRenameContainer() {
s.cliMock.On("RenameContainer", defaultNamespace, "old-name", "new-name").Return(nil).Once()

Expand Down
7 changes: 6 additions & 1 deletion cmd/cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ var (

namespace = app.Command("namespace", "namespace operations")
namespaceCreate = namespace.Command("create", "create new namespace")
namespaceCreateName = containerCreate.Arg("name", "name of the namespace to create").Required().String()
namespaceCreateName = namespaceCreate.Arg("name", "name of the namespace to create").Required().String()

namespaceRename = namespace.Command("rename", "rename the given namespace")
namespaceRenameOldName = namespaceRename.Arg("old-name", "the old name of the namespace").Required().String()
Expand All @@ -81,6 +81,10 @@ var (
containerCreate = container.Command("create", "create new container")
containerCreateName = containerCreate.Arg("name", "name of the container to create").Required().String()

containerMove = container.Command("move", "move container to another namespace")
containerMoveName = containerMove.Arg("name", "container namespace to move").Required().String()
containerMoveNamespace = containerMove.Arg("namespace", "destination namespace to move to").Required().String()

containerRename = container.Command("rename", "rename the given container")
containerRenameOldName = containerRename.Arg("old-name", "the old name of the container").Required().String()
containerRenameNewName = containerRename.Arg("new-name", "the new name of the container").Required().String()
Expand Down Expand Up @@ -218,6 +222,7 @@ func main() {
r.Register(namespaceDelete.FullCommand(), cliSvc.DeleteNamespace(*containerDeleteName))

r.Register(containerCreate.FullCommand(), cliSvc.CreateContainer(*namespaceName, *containerCreateName))
r.Register(containerMove.FullCommand(), cliSvc.MoveContainer(*namespaceName, *containerMoveName, *containerMoveNamespace))
r.Register(containerRename.FullCommand(), cliSvc.RenameContainer(*namespaceName, *containerRenameOldName, *containerRenameNewName))
r.Register(containerList.FullCommand(), cliSvc.ListContainers(*namespaceName))
r.Register(containerDelete.FullCommand(), cliSvc.DeleteContainer(*namespaceName, *containerDeleteName))
Expand Down
9 changes: 9 additions & 0 deletions manager/presenter/grpc/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,15 @@ func (h *handlers) CreateContainer(ctx context.Context, in *v1.CreateContainerRe
return &v1.CreateContainerResponse{}, nil
}

func (h *handlers) MoveContainer(ctx context.Context, in *v1.MoveContainerRequest) (*v1.MoveContainerResponse, error) {
err := h.svc.MoveContainer(ctx, in.GetNamespace(), in.GetContainerName(), in.GetDestinationNamespace())
if err != nil {
return nil, mapServiceError(err)
}

return &v1.MoveContainerResponse{}, nil
}

func (h *handlers) RenameContainer(ctx context.Context, in *v1.RenameContainerRequest) (*v1.RenameContainerResponse, error) {
err := h.svc.RenameContainer(ctx, in.GetNamespace(), in.GetOldName(), in.GetNewName())
if err != nil {
Expand Down
11 changes: 11 additions & 0 deletions manager/presenter/grpc/handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,17 @@ func (s *manageHandlersTestSuite) TestCreateContainer() {
s.Require().NoError(err)
}

func (s *manageHandlersTestSuite) TestMoveContainer() {
s.svcMock.On("MoveContainer", defaultNamespace, "test-container", "new-namespace").Return(nil).Once()

_, err := s.client.MoveContainer(s.ctx, &v1pb.MoveContainerRequest{
Namespace: defaultNamespace,
ContainerName: "test-container",
DestinationNamespace: "new-namespace",
})
s.Require().NoError(err)
}

func (s *manageHandlersTestSuite) TestCreateContainerNotFound() {
s.svcMock.On("CreateContainer", defaultNamespace, "test-container").Return(service.ErrNotFound).Once()

Expand Down
8 changes: 8 additions & 0 deletions manager/presenter/grpc/proto/v1/manager.proto
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ message CreateContainerRequest {
}
message CreateContainerResponse {}

message MoveContainerRequest {
string namespace = 1;
string container_name = 2;
string destination_namespace = 3;
}
message MoveContainerResponse {}

message RenameContainerRequest{
string namespace = 1;
string old_name = 2;
Expand Down Expand Up @@ -136,6 +143,7 @@ service ManageService {
rpc ListNamespaces(ListNamespacesRequest) returns (ListNamespacesResponse);

rpc CreateContainer(CreateContainerRequest) returns (CreateContainerResponse);
rpc MoveContainer(MoveContainerRequest) returns (MoveContainerResponse);
rpc RenameContainer(RenameContainerRequest) returns (RenameContainerResponse);
rpc DeleteContainer(DeleteContainerRequest) returns (DeleteContainerResponse);
rpc ListContainers(ListContainersRequest) returns (ListContainersResponse);
Expand Down
1 change: 1 addition & 0 deletions publisher/presenter/html/templates/container-list.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{{ $namespace := .Namespace }}
{{ template "header" . }}
<a href="..">..</a><br>
{{- range $container := .Containers }}
<a href="/{{ $namespace }}/{{ $container }}/">{{ $container }}/</a><br>
{{- else }}
Expand Down
1 change: 1 addition & 0 deletions publisher/presenter/html/testdata/containers.html.sample
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<h1>Container index (default)</h1>
<hr>

<a href="..">..</a><br>
<a href="/default/test-container-1/">test-container-1/</a><br>

<hr>
Expand Down
4 changes: 2 additions & 2 deletions repositories/cache/metadata/memcache/memcache.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ func (m *memcache) CreateContainer(ctx context.Context, namespace, name string)
return m.repo.CreateContainer(ctx, namespace, name)
}

func (m *memcache) RenameContainer(ctx context.Context, namespace, oldName, newName string) error {
return m.repo.RenameContainer(ctx, namespace, oldName, newName)
func (m *memcache) RenameContainer(ctx context.Context, namespace, oldName, newNamespace, newName string) error {
return m.repo.RenameContainer(ctx, namespace, oldName, newNamespace, newName)
}

func (m *memcache) ListContainers(ctx context.Context, namespace string) ([]string, error) {
Expand Down
6 changes: 3 additions & 3 deletions repositories/cache/metadata/memcache/memcache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,12 +180,12 @@ func (s *memcacheTestSuite) TestCreateContainer() {
}

func (s *memcacheTestSuite) TestRenameContainer() {
s.repoMock.On("RenameContainer", defaultNamespace, "old-name", "new-name").Return(nil).Twice()
s.repoMock.On("RenameContainer", defaultNamespace, "old-name", "new-namespace", "new-name").Return(nil).Twice()

err := s.cache.RenameContainer(s.ctx, defaultNamespace, "old-name", "new-name")
err := s.cache.RenameContainer(s.ctx, defaultNamespace, "old-name", "new-namespace", "new-name")
s.Require().NoError(err)

err = s.cache.RenameContainer(s.ctx, defaultNamespace, "old-name", "new-name")
err = s.cache.RenameContainer(s.ctx, defaultNamespace, "old-name", "new-namespace", "new-name")
s.Require().NoError(err)
}

Expand Down
2 changes: 1 addition & 1 deletion repositories/metadata/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type Repository interface {
DeleteNamespace(ctx context.Context, name string) error

CreateContainer(ctx context.Context, namespace, name string) error
RenameContainer(ctx context.Context, namespace, oldName, newName string) error
RenameContainer(ctx context.Context, namespace, oldName, newNamespace, newName string) error
ListContainers(ctx context.Context, namespace string) ([]string, error)
DeleteContainer(ctx context.Context, namespace, name string) error

Expand Down
4 changes: 2 additions & 2 deletions repositories/metadata/mock/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ func (m *Mock) CreateContainer(_ context.Context, namespace, name string) error
return args.Error(0)
}

func (m *Mock) RenameContainer(_ context.Context, namespace, oldName, newName string) error {
args := m.Called(namespace, oldName, newName)
func (m *Mock) RenameContainer(_ context.Context, namespace, oldName, newNamespace, newName string) error {
args := m.Called(namespace, oldName, newNamespace, newName)
return args.Error(0)
}

Expand Down
16 changes: 15 additions & 1 deletion repositories/metadata/postgresql/containers.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func (r *repository) CreateContainer(ctx context.Context, namespace, name string
return nil
}

func (r *repository) RenameContainer(ctx context.Context, namespace, oldName, newName string) error {
func (r *repository) RenameContainer(ctx context.Context, namespace, oldName, newNamespace, newName string) error {
tx, err := r.db.BeginTx(ctx, nil)
if err != nil {
return mapSQLErrors(err)
Expand All @@ -68,6 +68,19 @@ func (r *repository) RenameContainer(ctx context.Context, namespace, oldName, ne
return mapSQLErrors(err)
}

row, err = selectQueryRow(ctx, tx, psql.
Select("id").
From("namespaces").
Where(sq.Eq{"name": newNamespace}))
if err != nil {
return mapSQLErrors(err)
}

var newNamespaceID uint
if err := row.Scan(&newNamespaceID); err != nil {
return mapSQLErrors(err)
}

row, err = selectQueryRow(ctx, tx, psql.
Select("id").
From("containers").
Expand All @@ -87,6 +100,7 @@ func (r *repository) RenameContainer(ctx context.Context, namespace, oldName, ne
_, err = updateQuery(ctx, tx, psql.
Update("containers").
Set("name", newName).
Set("namespace_id", newNamespaceID).
Where(sq.Eq{
"id": containerID,
}),
Expand Down
13 changes: 10 additions & 3 deletions repositories/metadata/postgresql/containers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ package postgresql
import "github.com/teran/archived/repositories/metadata"

func (s *postgreSQLRepositoryTestSuite) TestContainerOperations() {
s.tp.On("Now").Return("2024-01-02T01:02:03Z").Times(3)
s.tp.On("Now").Return("2024-01-02T01:02:03Z").Times(4)

err := s.repo.CreateNamespace(s.ctx, "new-namespace")
s.Require().NoError(err)

list, err := s.repo.ListContainers(s.ctx, defaultNamespace)
s.Require().NoError(err)
Expand Down Expand Up @@ -38,15 +41,19 @@ func (s *postgreSQLRepositoryTestSuite) TestContainerOperations() {
"test-container5",
}, list)

err = s.repo.RenameContainer(s.ctx, defaultNamespace, "test-container5", "and-then-there-was-the-one")
err = s.repo.RenameContainer(s.ctx, defaultNamespace, "test-container5", "new-namespace", "and-then-there-was-the-one")
s.Require().NoError(err)

err = s.repo.RenameContainer(s.ctx, defaultNamespace, "not-existent", "some-name")
err = s.repo.RenameContainer(s.ctx, defaultNamespace, "not-existent", "new-namespace", "some-name")
s.Require().Error(err)
s.Require().Equal(metadata.ErrNotFound, err)

list, err = s.repo.ListContainers(s.ctx, defaultNamespace)
s.Require().NoError(err)
s.Require().Equal([]string{}, list)

list, err = s.repo.ListContainers(s.ctx, "new-namespace")
s.Require().NoError(err)
s.Require().Equal([]string{
"and-then-there-was-the-one",
}, list)
Expand Down
5 changes: 5 additions & 0 deletions service/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ func (m *Mock) CreateContainer(_ context.Context, namespace, name string) error
return args.Error(0)
}

func (m *Mock) MoveContainer(ctx context.Context, namespace, container, destNamespace string) error {
args := m.Called(namespace, container, destNamespace)
return args.Error(0)
}

func (m *Mock) RenameContainer(_ context.Context, namespace, oldName, newName string) error {
args := m.Called(namespace, oldName, newName)
return args.Error(0)
Expand Down
Loading

0 comments on commit 958fa22

Please sign in to comment.