Skip to content

Commit

Permalink
Merge pull request #15 from vshn/deletion-protection
Browse files Browse the repository at this point in the history
Add deletion protection controller for VSHNPostgreSQL
  • Loading branch information
zugao authored May 2, 2023
2 parents 4b6a2e5 + 70ac71b commit 54a9ae8
Show file tree
Hide file tree
Showing 53 changed files with 2,423 additions and 299 deletions.
15 changes: 11 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[![Version](https://img.shields.io/github/v/release/appuio/control-api)](https://github.com/appuio/control-api/releases)
[![GitHub downloads](https://img.shields.io/github/downloads/vshn/appcat-apiserver/total)](https://github.com/appuio/control-api/releases)

# appcat-apiserve
# appcat-apiserver

## Generate Kubernetes code

Expand Down Expand Up @@ -47,12 +47,12 @@ On some docker distributions the host IP is accessible via `host.docker.internal
For Lima distribution the host IP is accessible via `host.lima.internal`.

```bash
make local-debug

HOSTIP=$(docker inspect appcat-apiserver-v1.24.0-control-plane | jq '.[0].NetworkSettings.Networks.kind.Gateway')
# HOSTIP=host.docker.internal # On some docker distributions
# HOSTIP=host.lima.internal # On lima distributions

make local-debug

kind get kubeconfig --name appcat-apiserver-v1.24.0 > ~/.kube/config

cat <<EOF | sed -e "s/172.21.0.1/$HOSTIP/g" | kubectl apply -f -
Expand Down Expand Up @@ -93,10 +93,17 @@ make apply-test-cases

After the above steps just run the API server via IDE with the following arguments.
```
api --secure-port=9443 --kubeconfig ~/.kube/config --authentication-kubeconfig ~/.kube/config --authorization-kubeconfig ~/.kube/config --tls-cert-file=dev/certificates/apiserver.crt --tls-private-key-file=dev/certificates/apiserver.key
apiserver --secure-port=9443 --kubeconfig=$HOME/.kube/config --authentication-kubeconfig=$HOME/.kube/config --authorization-kubeconfig=$HOME/.kube/config --tls-cert-file=dev/certificates/apiserver.crt --tls-private-key-file=dev/certificates/apiserver.key
```

## Protobuf installation
Protocol Buffers (Protobuf) is a free and open-source cross-platform data format used to serialize structured data.
Kubernetes internally uses gRPC clients with protobuf serialization. APIServer objects when handled internally in K8S
need to implement protobuf interface. The implementation of the interface is done by [code-generator](https://github.com/kubernetes/code-generator). Two dependencies are required to use this tool [protoc](https://github.com/protocolbuffers/protobuf) and [protoc-gen-go](https://google.golang.org/protobuf/cmd/protoc-gen-go).

## Controller
A postgres controller has been added to this API Server. The controller takes care of database deletion protection.
The controller insures via a finalizer in composite `XVSHNPostgreSQL` resource that the backups are still available
when the database instance is deleted. When deletion retention expires, the backups with the remaining resources
are cleaned up automatically.
This controller addition to this repository is a temporary solution until we move to a common AppCat repository.
4 changes: 2 additions & 2 deletions apiserver/appcat/appcat.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ type appcatStorage struct {
compositions compositionProvider
}

func (s appcatStorage) New() runtime.Object {
func (s *appcatStorage) New() runtime.Object {
return &v1.AppCat{}
}

func (s appcatStorage) Destroy() {}
func (s *appcatStorage) Destroy() {}

var _ rest.Scoper = &appcatStorage{}
var _ rest.Storage = &appcatStorage{}
Expand Down
6 changes: 3 additions & 3 deletions apiserver/appcat/appcat_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package appcat
import (
crossplanev1 "github.com/crossplane/crossplane/apis/apiextensions/v1"
v1 "github.com/vshn/appcat-apiserver/apis/appcat/v1"
"github.com/vshn/appcat-apiserver/apiserver/appcat/mock"
"github.com/vshn/appcat-apiserver/test/mocks"
"k8s.io/apiserver/pkg/registry/rest"
"testing"

Expand All @@ -12,9 +12,9 @@ import (
)

// newMockedAppCatStorage is a mocked instance of AppCatStorage
func newMockedAppCatStorage(t *testing.T, ctrl *gomock.Controller) (rest.StandardStorage, *mock_appcat.MockcompositionProvider) {
func newMockedAppCatStorage(t *testing.T, ctrl *gomock.Controller) (rest.StandardStorage, *mocks.MockcompositionProvider) {
t.Helper()
comp := mock_appcat.NewMockcompositionProvider(ctrl)
comp := mocks.NewMockcompositionProvider(ctrl)
stor := &appcatStorage{
compositions: comp,
}
Expand Down
2 changes: 0 additions & 2 deletions apiserver/appcat/composition.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import (
)

// compositionProvider is an abstraction to interact with the K8s API
//
//go:generate go run github.com/golang/mock/mockgen -source=$GOFILE -destination=./mock/$GOFILE
type compositionProvider interface {
GetComposition(ctx context.Context, name string, options *metav1.GetOptions) (*v1.Composition, error)
ListCompositions(ctx context.Context, options *metainternalversion.ListOptions) (*v1.CompositionList, error)
Expand Down
2 changes: 1 addition & 1 deletion apiserver/appcat/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ import (

var _ rest.Creater = &appcatStorage{}

func (s appcatStorage) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
func (s *appcatStorage) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
return nil, fmt.Errorf("method not implemented")
}
2 changes: 1 addition & 1 deletion apiserver/appcat/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
var _ rest.GracefulDeleter = &appcatStorage{}
var _ rest.CollectionDeleter = &appcatStorage{}

func (s appcatStorage) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
func (s *appcatStorage) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
return &v1.AppCat{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Expand Down
2 changes: 1 addition & 1 deletion apiserver/appcat/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
var _ rest.Updater = &appcatStorage{}
var _ rest.CreaterUpdater = &appcatStorage{}

func (s appcatStorage) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
func (s *appcatStorage) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
return &v1.AppCat{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Expand Down
2 changes: 1 addition & 1 deletion apiserver/vshn/postgres/backup.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func New() restbuilder.ResourceHandlerProvider {
sgbackups: &kubeSGBackupProvider{
DynamicClient: dc.Resource(sgbackupGroupVersionResource),
},
vshnpostgresql: &kubeVSHNPostgresqlProvider{
vshnpostgresql: &kubeXVSHNPostgresqlProvider{
Client: c,
},
}, nil
Expand Down
25 changes: 17 additions & 8 deletions apiserver/vshn/postgres/backup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ package postgres

import (
"github.com/vshn/appcat-apiserver/apis/appcat/v1"
mock_postgres "github.com/vshn/appcat-apiserver/apiserver/vshn/postgres/mock"
"github.com/vshn/appcat-apiserver/test/mocks"

vshnv1 "github.com/vshn/component-appcat/apis/vshn/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
Expand All @@ -14,10 +15,10 @@ import (
)

// newMockedVSHNPostgresBackupStorage is a mocked instance of vshnPostgresBackup
func newMockedVSHNPostgresBackupStorage(t *testing.T, ctrl *gomock.Controller) (rest.StandardStorage, *mock_postgres.MocksgbackupProvider, *mock_postgres.MockvshnPostgresqlProvider) {
func newMockedVSHNPostgresBackupStorage(t *testing.T, ctrl *gomock.Controller) (rest.StandardStorage, *mocks.MocksgbackupProvider, *mocks.MockvshnPostgresqlProvider) {
t.Helper()
sgbackup := mock_postgres.NewMocksgbackupProvider(ctrl)
vshnpostgres := mock_postgres.NewMockvshnPostgresqlProvider(ctrl)
sgbackup := mocks.NewMocksgbackupProvider(ctrl)
vshnpostgres := mocks.NewMockvshnPostgresqlProvider(ctrl)
stor := &vshnPostgresBackupStorage{
sgbackups: sgbackup,
vshnpostgresql: vshnpostgres,
Expand Down Expand Up @@ -105,19 +106,27 @@ var (
},
}

vshnPostgreSQLInstances = &vshnv1.VSHNPostgreSQLList{
Items: []vshnv1.VSHNPostgreSQL{
vshnPostgreSQLInstances = &vshnv1.XVSHNPostgreSQLList{
Items: []vshnv1.XVSHNPostgreSQL{
{
ObjectMeta: metav1.ObjectMeta{
Name: "postgres-one",
Name: "postgres-one-tty",
Labels: map[string]string{
claimNameLabel: "postgres-one",
claimNamespaceLabel: "namespace-claim",
},
},
Status: vshnv1.VSHNPostgreSQLStatus{
InstanceNamespace: "namespace-one",
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "postgres-two",
Name: "postgres-two-bbf",
Labels: map[string]string{
claimNameLabel: "postgres-two",
claimNamespaceLabel: "namespace-claim",
},
},
Status: vshnv1.VSHNPostgreSQLStatus{
InstanceNamespace: "namespace-two",
Expand Down
4 changes: 2 additions & 2 deletions apiserver/vshn/postgres/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func (v *vshnPostgresBackupStorage) Get(ctx context.Context, name string, _ *met
return nil, fmt.Errorf("cannot get namespace from resource")
}

instances, err := v.vshnpostgresql.ListVSHNPostgreSQL(ctx, namespace)
instances, err := v.vshnpostgresql.ListXVSHNPostgreSQL(ctx, namespace)
if err != nil {
return nil, fmt.Errorf("cannot list VSHNPostgreSQL instances")
}
Expand All @@ -37,7 +37,7 @@ func (v *vshnPostgresBackupStorage) Get(ctx context.Context, name string, _ *met
return nil, err
}

vshnBackup = v1.NewVSHNPostgresBackup(backupInfo, value.Name, namespace)
vshnBackup = v1.NewVSHNPostgresBackup(backupInfo, value.Labels[claimNameLabel], namespace)
}

if vshnBackup == nil {
Expand Down
16 changes: 8 additions & 8 deletions apiserver/vshn/postgres/get_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package postgres

import (
"github.com/vshn/appcat-apiserver/apis/appcat/v1"
mock_postgres "github.com/vshn/appcat-apiserver/apiserver/vshn/postgres/mock"
"github.com/vshn/appcat-apiserver/test/mocks"
vshnv1 "github.com/vshn/component-appcat/apis/vshn/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"testing"
Expand All @@ -17,17 +17,17 @@ import (
func TestVSHNPostgresBackupStorage_Get(t *testing.T) {
tests := map[string]struct {
name string
postgresqls *vshnv1.VSHNPostgreSQLList
postgresqls *vshnv1.XVSHNPostgreSQLList
backupInfo *v1.SGBackupInfo
backupInfoCalls func(mock_postgres.MocksgbackupProvider, string)
backupInfoCalls func(mocks.MocksgbackupProvider, string)
vshnPostgresBackup *v1.VSHNPostgresBackup
err error
}{
"GivenAListOfPostgresAndBackups_ThenVSHNPostgresBackup": {
name: "one",
postgresqls: vshnPostgreSQLInstances,
backupInfo: backupInfoOne,
backupInfoCalls: func(provider mock_postgres.MocksgbackupProvider, name string) {
backupInfoCalls: func(provider mocks.MocksgbackupProvider, name string) {
provider.EXPECT().
GetSGBackup(gomock.Any(), name, "namespace-one").
Return(backupInfoOne, nil).
Expand All @@ -45,7 +45,7 @@ func TestVSHNPostgresBackupStorage_Get(t *testing.T) {
name: "one",
postgresqls: vshnPostgreSQLInstances,
backupInfo: nil,
backupInfoCalls: func(provider mock_postgres.MocksgbackupProvider, name string) {
backupInfoCalls: func(provider mocks.MocksgbackupProvider, name string) {
provider.EXPECT().
GetSGBackup(gomock.Any(), name, "namespace-one").
Return(nil, apierrors.NewNotFound(v1.GetGroupResource(v1.ResourceBackup), name)).
Expand All @@ -61,9 +61,9 @@ func TestVSHNPostgresBackupStorage_Get(t *testing.T) {
},
"GivenNoPostgresInstances_ThenErrNotFound": {
name: "one",
postgresqls: &vshnv1.VSHNPostgreSQLList{},
postgresqls: &vshnv1.XVSHNPostgreSQLList{},
backupInfo: nil,
backupInfoCalls: func(provider mock_postgres.MocksgbackupProvider, name string) {
backupInfoCalls: func(provider mocks.MocksgbackupProvider, name string) {
provider.EXPECT().
GetSGBackup(gomock.Any(), gomock.Any(), gomock.Any()).
Times(0)
Expand All @@ -81,7 +81,7 @@ func TestVSHNPostgresBackupStorage_Get(t *testing.T) {
s, backupProvider, postgresProvider := newMockedVSHNPostgresBackupStorage(t, ctrl)

postgresProvider.EXPECT().
ListVSHNPostgreSQL(gomock.Any(), gomock.Any()).
ListXVSHNPostgreSQL(gomock.Any(), gomock.Any()).
Return(tc.postgresqls, nil).
Times(1)

Expand Down
8 changes: 4 additions & 4 deletions apiserver/vshn/postgres/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func (v *vshnPostgresBackupStorage) List(ctx context.Context, options *metainter
return nil, fmt.Errorf("cannot get namespace from resource")
}

instances, err := v.vshnpostgresql.ListVSHNPostgreSQL(ctx, namespace)
instances, err := v.vshnpostgresql.ListXVSHNPostgreSQL(ctx, namespace)
if err != nil {
return nil, fmt.Errorf("cannot list VSHNPostgreSQL instances %v", err)
}
Expand All @@ -38,7 +38,7 @@ func (v *vshnPostgresBackupStorage) List(ctx context.Context, options *metainter
return nil, apiserver.ResolveError(v1.GetGroupResource(v1.ResourceBackup), err)
}
for _, b := range *bis {
vb := v1.NewVSHNPostgresBackup(&b, value.Name, namespace)
vb := v1.NewVSHNPostgresBackup(&b, value.Labels[claimNameLabel], namespace)
if vb != nil {
backups.Items = append(backups.Items, *vb)
}
Expand All @@ -57,7 +57,7 @@ func (v *vshnPostgresBackupStorage) Watch(ctx context.Context, options *metainte
return nil, fmt.Errorf("cannot get namespace from resource")
}

instances, err := v.vshnpostgresql.ListVSHNPostgreSQL(ctx, namespace)
instances, err := v.vshnpostgresql.ListXVSHNPostgreSQL(ctx, namespace)
if err != nil {
return nil, fmt.Errorf("cannot list VSHNPostgreSQL instances")
}
Expand Down Expand Up @@ -85,7 +85,7 @@ func (v *vshnPostgresBackupStorage) Watch(ctx context.Context, options *metainte
db := ""
for _, value := range instances.Items {
if value.Status.InstanceNamespace == sgbackupInfo.Namespace {
db = value.Name
db = value.Labels[claimNameLabel]
}
}

Expand Down
Loading

0 comments on commit 54a9ae8

Please sign in to comment.