From 3a9e61eb2cf962e6e37b55985efde48ccd9804b0 Mon Sep 17 00:00:00 2001 From: Simon Beck Date: Tue, 20 Jun 2023 11:36:40 +0200 Subject: [PATCH] Add Redis backup listing It's now possible to list the backups of Redis instances via the API server. --- .gitignore | 3 + apis/appcat/v1/generated.pb.go | 800 ++++++++++++++++++++-- apis/appcat/v1/register.go | 2 + apis/appcat/v1/vshn_redis_backup_types.go | 72 ++ apis/appcat/v1/zz_generated.deepcopy.go | 74 ++ apis/vshn/v1/dbaas_vshn_redis.go | 9 + apis/vshn/v1/groupversion_info.go | 9 +- apis/vshn/v1/zz_generated.deepcopy.go | 32 + cmd/apiserver.go | 37 +- config/apiserver/role.yaml | 9 + go.mod | 3 +- go.sum | 2 + pkg/apiserver/common.go | 48 ++ pkg/apiserver/vshn/k8up/k8up.go | 99 +++ pkg/apiserver/vshn/postgres/list.go | 5 +- pkg/apiserver/vshn/postgres/multiwatch.go | 51 -- pkg/apiserver/vshn/redis/backup.go | 74 ++ pkg/apiserver/vshn/redis/common_test.go | 64 ++ pkg/apiserver/vshn/redis/create.go | 16 + pkg/apiserver/vshn/redis/delete.go | 26 + pkg/apiserver/vshn/redis/get.go | 55 ++ pkg/apiserver/vshn/redis/get_test.go | 110 +++ pkg/apiserver/vshn/redis/list.go | 61 ++ pkg/apiserver/vshn/redis/list_test.go | 154 +++++ pkg/apiserver/vshn/redis/table.go | 59 ++ pkg/apiserver/vshn/redis/table_test.go | 65 ++ pkg/apiserver/vshn/redis/update.go | 18 + pkg/apiserver/vshn/redis/vshnredis.go | 28 + pkg/apiserver/vshn/redis/watch.go | 76 ++ pkg/scheme.go | 6 +- 30 files changed, 1936 insertions(+), 131 deletions(-) create mode 100644 apis/appcat/v1/vshn_redis_backup_types.go create mode 100644 pkg/apiserver/vshn/k8up/k8up.go delete mode 100644 pkg/apiserver/vshn/postgres/multiwatch.go create mode 100644 pkg/apiserver/vshn/redis/backup.go create mode 100644 pkg/apiserver/vshn/redis/common_test.go create mode 100644 pkg/apiserver/vshn/redis/create.go create mode 100644 pkg/apiserver/vshn/redis/delete.go create mode 100644 pkg/apiserver/vshn/redis/get.go create mode 100644 pkg/apiserver/vshn/redis/get_test.go create mode 100644 pkg/apiserver/vshn/redis/list.go create mode 100644 pkg/apiserver/vshn/redis/list_test.go create mode 100644 pkg/apiserver/vshn/redis/table.go create mode 100644 pkg/apiserver/vshn/redis/table_test.go create mode 100644 pkg/apiserver/vshn/redis/update.go create mode 100644 pkg/apiserver/vshn/redis/vshnredis.go create mode 100644 pkg/apiserver/vshn/redis/watch.go diff --git a/.gitignore b/.gitignore index 0b6c2d488..1f4511c5e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ dist/ # Binaries for programs and plugins appcat +# But don't ignore the appcat APIS! +!apis/appcat # temp file, editor and IDE paraphernalia .idea @@ -14,6 +16,7 @@ appcat # debug apiserver.local.config +__debug_bin # Kubebuilder /apis/generated/ diff --git a/apis/appcat/v1/generated.pb.go b/apis/appcat/v1/generated.pb.go index 32aa65b3a..ce64b1b20 100644 --- a/apis/appcat/v1/generated.pb.go +++ b/apis/appcat/v1/generated.pb.go @@ -253,10 +253,94 @@ func (m *VSHNPostgresBackupStatus) XXX_DiscardUnknown() { var xxx_messageInfo_VSHNPostgresBackupStatus proto.InternalMessageInfo +func (m *VSHNRedisBackup) Reset() { *m = VSHNRedisBackup{} } +func (*VSHNRedisBackup) ProtoMessage() {} +func (*VSHNRedisBackup) Descriptor() ([]byte, []int) { + return fileDescriptor_3f37f7f54c7900be, []int{8} +} +func (m *VSHNRedisBackup) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *VSHNRedisBackup) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *VSHNRedisBackup) XXX_Merge(src proto.Message) { + xxx_messageInfo_VSHNRedisBackup.Merge(m, src) +} +func (m *VSHNRedisBackup) XXX_Size() int { + return m.Size() +} +func (m *VSHNRedisBackup) XXX_DiscardUnknown() { + xxx_messageInfo_VSHNRedisBackup.DiscardUnknown(m) +} + +var xxx_messageInfo_VSHNRedisBackup proto.InternalMessageInfo + +func (m *VSHNRedisBackupList) Reset() { *m = VSHNRedisBackupList{} } +func (*VSHNRedisBackupList) ProtoMessage() {} +func (*VSHNRedisBackupList) Descriptor() ([]byte, []int) { + return fileDescriptor_3f37f7f54c7900be, []int{9} +} +func (m *VSHNRedisBackupList) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *VSHNRedisBackupList) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *VSHNRedisBackupList) XXX_Merge(src proto.Message) { + xxx_messageInfo_VSHNRedisBackupList.Merge(m, src) +} +func (m *VSHNRedisBackupList) XXX_Size() int { + return m.Size() +} +func (m *VSHNRedisBackupList) XXX_DiscardUnknown() { + xxx_messageInfo_VSHNRedisBackupList.DiscardUnknown(m) +} + +var xxx_messageInfo_VSHNRedisBackupList proto.InternalMessageInfo + +func (m *VSHNRedisBackupStatus) Reset() { *m = VSHNRedisBackupStatus{} } +func (*VSHNRedisBackupStatus) ProtoMessage() {} +func (*VSHNRedisBackupStatus) Descriptor() ([]byte, []int) { + return fileDescriptor_3f37f7f54c7900be, []int{10} +} +func (m *VSHNRedisBackupStatus) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *VSHNRedisBackupStatus) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *VSHNRedisBackupStatus) XXX_Merge(src proto.Message) { + xxx_messageInfo_VSHNRedisBackupStatus.Merge(m, src) +} +func (m *VSHNRedisBackupStatus) XXX_Size() int { + return m.Size() +} +func (m *VSHNRedisBackupStatus) XXX_DiscardUnknown() { + xxx_messageInfo_VSHNRedisBackupStatus.DiscardUnknown(m) +} + +var xxx_messageInfo_VSHNRedisBackupStatus proto.InternalMessageInfo + func (m *VSHNSize) Reset() { *m = VSHNSize{} } func (*VSHNSize) ProtoMessage() {} func (*VSHNSize) Descriptor() ([]byte, []int) { - return fileDescriptor_3f37f7f54c7900be, []int{8} + return fileDescriptor_3f37f7f54c7900be, []int{11} } func (m *VSHNSize) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -292,6 +376,9 @@ func init() { proto.RegisterType((*VSHNPostgresBackup)(nil), "github.com.vshn.appcat.apis.appcat.v1.VSHNPostgresBackup") proto.RegisterType((*VSHNPostgresBackupList)(nil), "github.com.vshn.appcat.apis.appcat.v1.VSHNPostgresBackupList") proto.RegisterType((*VSHNPostgresBackupStatus)(nil), "github.com.vshn.appcat.apis.appcat.v1.VSHNPostgresBackupStatus") + proto.RegisterType((*VSHNRedisBackup)(nil), "github.com.vshn.appcat.apis.appcat.v1.VSHNRedisBackup") + proto.RegisterType((*VSHNRedisBackupList)(nil), "github.com.vshn.appcat.apis.appcat.v1.VSHNRedisBackupList") + proto.RegisterType((*VSHNRedisBackupStatus)(nil), "github.com.vshn.appcat.apis.appcat.v1.VSHNRedisBackupStatus") proto.RegisterType((*VSHNSize)(nil), "github.com.vshn.appcat.apis.appcat.v1.VSHNSize") } @@ -300,60 +387,66 @@ func init() { } var fileDescriptor_3f37f7f54c7900be = []byte{ - // 843 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x56, 0x4d, 0x6f, 0x1b, 0x45, - 0x18, 0xf6, 0xda, 0xce, 0x47, 0xc7, 0x81, 0xa4, 0x23, 0x3e, 0x16, 0x4b, 0x6c, 0xa3, 0x95, 0x8a, - 0x7a, 0xc9, 0x2c, 0x09, 0x08, 0x85, 0x5c, 0xaa, 0x6e, 0x12, 0x41, 0x80, 0x86, 0x30, 0x01, 0x84, - 0xf8, 0x12, 0xe3, 0xf5, 0x74, 0x33, 0x6c, 0x76, 0x67, 0xb5, 0x33, 0x76, 0x49, 0x4f, 0xfd, 0x09, - 0xfc, 0x0e, 0x7e, 0x04, 0xdc, 0x50, 0x8e, 0x3d, 0xa1, 0x9e, 0x0a, 0x31, 0xff, 0x82, 0x13, 0x9a, - 0x8f, 0xf5, 0x6e, 0xe2, 0x04, 0x1c, 0xab, 0xea, 0x6d, 0xe6, 0x9d, 0x7d, 0x9e, 0xe7, 0x7d, 0xdf, - 0x79, 0x9f, 0xb1, 0xc1, 0x5d, 0xf4, 0x90, 0x17, 0x49, 0x20, 0xd3, 0x3c, 0x88, 0x99, 0x3c, 0x1a, - 0xf4, 0x50, 0xc4, 0xd3, 0x60, 0x28, 0x8e, 0xb2, 0x80, 0xe4, 0x79, 0x44, 0x64, 0x40, 0x72, 0x26, - 0xca, 0xf5, 0x70, 0x3d, 0x88, 0x69, 0x46, 0x0b, 0x22, 0x69, 0x1f, 0xe5, 0x05, 0x97, 0x1c, 0xde, - 0xae, 0x60, 0x48, 0xc1, 0x90, 0xf9, 0x14, 0x29, 0x58, 0xb9, 0x1e, 0xae, 0x77, 0xd7, 0x6a, 0xec, - 0x31, 0x8f, 0x79, 0xa0, 0xd1, 0xbd, 0xc1, 0x03, 0xbd, 0xd3, 0x1b, 0xbd, 0x32, 0xac, 0xdd, 0x77, - 0x93, 0x4d, 0x81, 0x18, 0x57, 0xe2, 0x29, 0x89, 0x8e, 0x58, 0x46, 0x8b, 0x93, 0x20, 0x4f, 0x62, - 0x93, 0x4d, 0x4a, 0x25, 0xb9, 0x24, 0x97, 0x6e, 0x70, 0x15, 0xaa, 0x18, 0x64, 0x92, 0xa5, 0x74, - 0x02, 0xf0, 0xde, 0xff, 0x01, 0x44, 0x74, 0x44, 0x53, 0x72, 0x11, 0xe7, 0xff, 0xd6, 0x06, 0xf3, - 0xf7, 0xf2, 0x7c, 0x9b, 0x48, 0xf8, 0x03, 0x58, 0x54, 0xe9, 0xf4, 0x89, 0x24, 0xae, 0xb3, 0xea, - 0xdc, 0xe9, 0x6c, 0xbc, 0x8d, 0x0c, 0x2b, 0xaa, 0xb3, 0xa2, 0x3c, 0x89, 0x4d, 0x4f, 0xd4, 0xd7, - 0x68, 0xb8, 0x8e, 0x3e, 0xed, 0xfd, 0x48, 0x23, 0x79, 0x9f, 0x4a, 0x12, 0xc2, 0xd3, 0x67, 0xb7, - 0x1a, 0xa3, 0x67, 0xb7, 0x40, 0x15, 0xc3, 0x63, 0x56, 0x98, 0x83, 0x85, 0x3e, 0x95, 0x84, 0x1d, - 0x0b, 0xb7, 0xb9, 0xda, 0xba, 0xd3, 0xd9, 0xd8, 0x42, 0x53, 0xf5, 0x1c, 0x99, 0x0c, 0xd1, 0x8e, - 0x01, 0xef, 0x66, 0xb2, 0x38, 0x09, 0xbb, 0x56, 0x6a, 0xc1, 0x46, 0xff, 0xa9, 0x96, 0xb8, 0x94, - 0x81, 0xdf, 0x81, 0xb9, 0xfc, 0x98, 0x64, 0xc2, 0x6d, 0x69, 0xbd, 0xcd, 0xeb, 0xe9, 0x1d, 0x28, - 0xa8, 0x51, 0x7b, 0xc9, 0xaa, 0xcd, 0xe9, 0x18, 0x36, 0xac, 0xf0, 0x1b, 0x30, 0x2f, 0x24, 0x91, - 0x03, 0xe1, 0xb6, 0x75, 0xc3, 0xde, 0xb9, 0x16, 0xff, 0xa1, 0x86, 0x86, 0x2f, 0x5b, 0xea, 0x79, - 0xb3, 0xc7, 0x96, 0xb2, 0xbb, 0x05, 0x96, 0xea, 0x05, 0xc3, 0x15, 0xd0, 0x4a, 0xe8, 0x89, 0xbe, - 0x9a, 0x1b, 0x58, 0x2d, 0xe1, 0x2b, 0x60, 0x6e, 0x48, 0x8e, 0x07, 0xd4, 0x6d, 0xea, 0x98, 0xd9, - 0x6c, 0x35, 0x37, 0x9d, 0x2e, 0x03, 0xa0, 0x4a, 0xfe, 0x12, 0xe4, 0x6e, 0x1d, 0xd9, 0xd9, 0x08, - 0xa6, 0xcc, 0xfb, 0xcb, 0xc3, 0x0f, 0xf7, 0x15, 0x6f, 0x4d, 0xca, 0xff, 0xd5, 0x01, 0xc0, 0xd4, - 0xf3, 0x09, 0x13, 0x12, 0x7e, 0x3b, 0x31, 0x45, 0x68, 0xba, 0x29, 0x52, 0x68, 0x3d, 0x43, 0x2b, - 0xb6, 0x1f, 0x8b, 0x65, 0xa4, 0x36, 0x41, 0x18, 0xcc, 0x31, 0x49, 0xd3, 0x72, 0x7e, 0xd6, 0xae, - 0xd5, 0xef, 0xea, 0x12, 0xf7, 0x14, 0x07, 0x36, 0x54, 0xfe, 0x67, 0x60, 0xa9, 0x7e, 0x1f, 0xf0, - 0x1e, 0x58, 0x8e, 0x78, 0x9a, 0x73, 0xc1, 0x24, 0xe3, 0xd9, 0x3e, 0x49, 0xa9, 0xe9, 0x5c, 0xf8, - 0xba, 0x85, 0x2f, 0x6f, 0x9f, 0x3f, 0xc6, 0x17, 0xbf, 0xf7, 0x7f, 0x6f, 0x82, 0xa5, 0xc3, 0x0f, - 0x42, 0x12, 0x25, 0x83, 0x7c, 0x2f, 0x7b, 0xc0, 0x61, 0x1f, 0x00, 0x3e, 0x76, 0xc4, 0x73, 0x75, - 0x57, 0x8d, 0x17, 0x7e, 0x05, 0x16, 0xf2, 0x82, 0x47, 0x54, 0x08, 0x7b, 0xaf, 0x6b, 0x57, 0x4a, - 0xd8, 0x67, 0x01, 0x61, 0xf2, 0x70, 0xf7, 0x27, 0x49, 0x33, 0xc1, 0x78, 0x16, 0x2e, 0x97, 0x96, - 0x3a, 0x30, 0x2c, 0xb8, 0xa4, 0x83, 0x43, 0x70, 0xb3, 0x37, 0xae, 0xa6, 0x48, 0x89, 0xaa, 0xd4, - 0x6d, 0xcd, 0xa2, 0xf1, 0x86, 0xd5, 0xb8, 0x19, 0x5e, 0xe4, 0xc3, 0x93, 0x12, 0xfe, 0x63, 0x07, - 0x2c, 0x96, 0x43, 0x07, 0x57, 0x41, 0x3b, 0xe3, 0xb2, 0xbc, 0x8d, 0x25, 0x4b, 0xd4, 0xde, 0xe7, - 0x92, 0x62, 0x7d, 0x02, 0x31, 0x68, 0x0b, 0xf6, 0x68, 0x96, 0xa9, 0x3e, 0x64, 0x8f, 0x68, 0x35, - 0x1f, 0x1f, 0xa9, 0x2d, 0xd6, 0x5c, 0xfe, 0x9f, 0x0e, 0x80, 0x3a, 0x05, 0x2e, 0x64, 0x5c, 0x50, - 0x61, 0xf2, 0x7e, 0x01, 0xaf, 0x65, 0x3c, 0x7e, 0x5c, 0x4c, 0x39, 0x77, 0xaf, 0x63, 0xd2, 0x73, - 0xc9, 0xfe, 0xf7, 0x43, 0xe3, 0xff, 0xe1, 0x80, 0xd7, 0x26, 0x41, 0x2f, 0xc0, 0xcd, 0xdf, 0x9f, - 0x77, 0xf3, 0xfb, 0x33, 0x17, 0x78, 0x85, 0xb3, 0x7f, 0x69, 0x02, 0xf7, 0xaa, 0x6e, 0xc0, 0xcf, - 0x2b, 0xb3, 0x38, 0xb3, 0x0c, 0x72, 0xe7, 0x52, 0xa3, 0x14, 0x97, 0x19, 0x65, 0x26, 0x33, 0xbe, - 0x3a, 0xad, 0x49, 0xe0, 0x0e, 0x58, 0x51, 0xed, 0xec, 0x11, 0x41, 0xf7, 0x32, 0x21, 0x49, 0x16, - 0x51, 0xed, 0xcd, 0x1b, 0xa1, 0x6b, 0xdb, 0xb2, 0xb2, 0x73, 0xe1, 0x1c, 0x4f, 0x20, 0x7c, 0x61, - 0x9c, 0xa6, 0x26, 0x1f, 0xbe, 0x09, 0x5a, 0x51, 0x3e, 0xb0, 0x46, 0xeb, 0x58, 0x92, 0xd6, 0xf6, - 0xc1, 0x17, 0x58, 0xc5, 0x95, 0x11, 0xfb, 0x4c, 0x24, 0xe6, 0x67, 0xa7, 0x32, 0xe2, 0x0e, 0x13, - 0x09, 0xd6, 0x27, 0xf0, 0x2d, 0x30, 0x9f, 0xd2, 0x94, 0x17, 0x27, 0x36, 0x91, 0xf1, 0xe8, 0xdd, - 0xd7, 0x51, 0x6c, 0x4f, 0xc3, 0x8f, 0x4f, 0xcf, 0xbc, 0xc6, 0x93, 0x33, 0xaf, 0xf1, 0xf4, 0xcc, - 0x6b, 0x3c, 0x1e, 0x79, 0xce, 0xe9, 0xc8, 0x73, 0x9e, 0x8c, 0x3c, 0xe7, 0xe9, 0xc8, 0x73, 0xfe, - 0x1a, 0x79, 0xce, 0xcf, 0x7f, 0x7b, 0x8d, 0xaf, 0x6f, 0x4f, 0xf5, 0x87, 0xee, 0xdf, 0x00, 0x00, - 0x00, 0xff, 0xff, 0xc7, 0x8e, 0x51, 0xc5, 0x02, 0x0a, 0x00, 0x00, + // 943 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0xdd, 0x6e, 0x1b, 0x45, + 0x14, 0xf6, 0xda, 0xce, 0x4f, 0x8f, 0x03, 0x49, 0x07, 0x0a, 0xc6, 0x12, 0x6e, 0x64, 0xa9, 0xa8, + 0x42, 0x64, 0x97, 0x04, 0x54, 0x85, 0x08, 0xa9, 0xea, 0xc6, 0x11, 0x04, 0xda, 0x10, 0x26, 0x05, + 0x21, 0x0a, 0x88, 0xf1, 0xee, 0xd4, 0x19, 0x9c, 0xdd, 0x59, 0xed, 0x8c, 0x5d, 0xd2, 0xab, 0x3e, + 0x02, 0xcf, 0xc1, 0x0d, 0x6f, 0x00, 0x77, 0x28, 0x97, 0xb9, 0x42, 0xe5, 0xa6, 0x10, 0xf3, 0x16, + 0x5c, 0xa1, 0xd9, 0x99, 0xb5, 0xd7, 0x7f, 0xd4, 0xb6, 0xaa, 0xdc, 0xcd, 0xcf, 0x7e, 0xdf, 0x77, + 0xce, 0x99, 0xef, 0x1c, 0xd9, 0x70, 0xdb, 0x7e, 0xc4, 0xe3, 0x96, 0x23, 0x83, 0xc8, 0x69, 0x32, + 0x79, 0xdc, 0x6e, 0xd8, 0x1e, 0x0f, 0x9c, 0x8e, 0x38, 0x0e, 0x1d, 0x12, 0x45, 0x1e, 0x91, 0x0e, + 0x89, 0x98, 0x48, 0xd7, 0x9d, 0x4d, 0xa7, 0x49, 0x43, 0x1a, 0x13, 0x49, 0x7d, 0x3b, 0x8a, 0xb9, + 0xe4, 0xe8, 0x46, 0x1f, 0x66, 0x2b, 0x98, 0xad, 0x3f, 0xb5, 0x15, 0x2c, 0x5d, 0x77, 0x36, 0x2b, + 0x1b, 0x19, 0xf6, 0x26, 0x6f, 0x72, 0x27, 0x41, 0x37, 0xda, 0x0f, 0x93, 0x5d, 0xb2, 0x49, 0x56, + 0x9a, 0xb5, 0xf2, 0x7e, 0x6b, 0x5b, 0xd8, 0x8c, 0x2b, 0xf1, 0x80, 0x78, 0xc7, 0x2c, 0xa4, 0xf1, + 0xa9, 0x13, 0xb5, 0x9a, 0x3a, 0x9a, 0x80, 0x4a, 0x32, 0x26, 0x96, 0x8a, 0x33, 0x09, 0x15, 0xb7, + 0x43, 0xc9, 0x02, 0x3a, 0x02, 0xb8, 0xf5, 0x3c, 0x80, 0xf0, 0x8e, 0x69, 0x40, 0x86, 0x71, 0xb5, + 0xdf, 0x8a, 0xb0, 0x78, 0x27, 0x8a, 0x76, 0x89, 0x44, 0xdf, 0xc3, 0xb2, 0x0a, 0xc7, 0x27, 0x92, + 0x94, 0xad, 0x75, 0xeb, 0x66, 0x69, 0xeb, 0x5d, 0x5b, 0xb3, 0xda, 0x59, 0x56, 0x3b, 0x6a, 0x35, + 0x75, 0x4d, 0xd4, 0xd7, 0x76, 0x67, 0xd3, 0xfe, 0xac, 0xf1, 0x03, 0xf5, 0xe4, 0x3d, 0x2a, 0x89, + 0x8b, 0xce, 0x9e, 0x5d, 0xcf, 0x75, 0x9f, 0x5d, 0x87, 0xfe, 0x19, 0xee, 0xb1, 0xa2, 0x08, 0x96, + 0x7c, 0x2a, 0x09, 0x3b, 0x11, 0xe5, 0xfc, 0x7a, 0xe1, 0x66, 0x69, 0x6b, 0xc7, 0x9e, 0xaa, 0xe6, + 0xb6, 0x8e, 0xd0, 0xae, 0x6b, 0xf0, 0x5e, 0x28, 0xe3, 0x53, 0xb7, 0x62, 0xa4, 0x96, 0xcc, 0xe9, + 0xbf, 0xfd, 0x25, 0x4e, 0x65, 0xd0, 0xb7, 0xb0, 0x10, 0x9d, 0x90, 0x50, 0x94, 0x0b, 0x89, 0xde, + 0xf6, 0x6c, 0x7a, 0x87, 0x0a, 0xaa, 0xd5, 0x5e, 0x32, 0x6a, 0x0b, 0xc9, 0x19, 0xd6, 0xac, 0xe8, + 0x01, 0x2c, 0x0a, 0x49, 0x64, 0x5b, 0x94, 0x8b, 0x49, 0xc1, 0xde, 0x9b, 0x89, 0xff, 0x28, 0x81, + 0xba, 0x2f, 0x1b, 0xea, 0x45, 0xbd, 0xc7, 0x86, 0xb2, 0xb2, 0x03, 0x2b, 0xd9, 0x84, 0xd1, 0x1a, + 0x14, 0x5a, 0xf4, 0x34, 0x79, 0x9a, 0x2b, 0x58, 0x2d, 0xd1, 0xab, 0xb0, 0xd0, 0x21, 0x27, 0x6d, + 0x5a, 0xce, 0x27, 0x67, 0x7a, 0xb3, 0x93, 0xdf, 0xb6, 0x2a, 0x0c, 0xa0, 0x1f, 0xfc, 0x18, 0xe4, + 0x5e, 0x16, 0x59, 0xda, 0x72, 0xa6, 0x8c, 0xfb, 0xcb, 0xa3, 0x8f, 0x0f, 0x14, 0x6f, 0x46, 0xaa, + 0xf6, 0xab, 0x05, 0xa0, 0xf3, 0xb9, 0xcb, 0x84, 0x44, 0xdf, 0x8c, 0xb8, 0xc8, 0x9e, 0xce, 0x45, + 0x0a, 0x9d, 0x78, 0x68, 0xcd, 0xd4, 0x63, 0x39, 0x3d, 0xc9, 0x38, 0x08, 0xc3, 0x02, 0x93, 0x34, + 0x48, 0xfd, 0xb3, 0x31, 0x53, 0xbd, 0xfb, 0x8f, 0xb8, 0xaf, 0x38, 0xb0, 0xa6, 0xaa, 0x7d, 0x0e, + 0x2b, 0xd9, 0xf7, 0x40, 0x77, 0x60, 0xd5, 0xe3, 0x41, 0xc4, 0x05, 0x93, 0x8c, 0x87, 0x07, 0x24, + 0xa0, 0xba, 0x72, 0xee, 0xeb, 0x06, 0xbe, 0xba, 0x3b, 0x78, 0x8d, 0x87, 0xbf, 0xaf, 0xfd, 0x9e, + 0x87, 0x95, 0xa3, 0x8f, 0x5c, 0xe2, 0xb5, 0xda, 0xd1, 0x7e, 0xf8, 0x90, 0x23, 0x1f, 0x80, 0xf7, + 0x3a, 0xe2, 0x85, 0x76, 0x57, 0x86, 0x17, 0x7d, 0x05, 0x4b, 0x51, 0xcc, 0x3d, 0x2a, 0x84, 0x79, + 0xd7, 0x8d, 0x89, 0x12, 0x66, 0x2c, 0xd8, 0x98, 0x3c, 0xda, 0xfb, 0x51, 0xd2, 0x50, 0x30, 0x1e, + 0xba, 0xab, 0x69, 0x4b, 0x1d, 0x6a, 0x16, 0x9c, 0xd2, 0xa1, 0x0e, 0x5c, 0x6d, 0xf4, 0xb2, 0x89, + 0x03, 0xa2, 0x32, 0x2d, 0x17, 0xe6, 0xd1, 0x78, 0xc3, 0x68, 0x5c, 0x75, 0x87, 0xf9, 0xf0, 0xa8, + 0x44, 0xed, 0x89, 0x05, 0xcb, 0xa9, 0xe9, 0xd0, 0x3a, 0x14, 0x43, 0x2e, 0xd3, 0xd7, 0x58, 0x31, + 0x44, 0xc5, 0x03, 0x2e, 0x29, 0x4e, 0x6e, 0x10, 0x86, 0xa2, 0x60, 0x8f, 0xe7, 0x71, 0xf5, 0x11, + 0x7b, 0x4c, 0xfb, 0xfe, 0xf8, 0x44, 0x6d, 0x71, 0xc2, 0x55, 0xfb, 0xcb, 0x02, 0x94, 0x84, 0xc0, + 0x85, 0x6c, 0xc6, 0x54, 0xe8, 0xb8, 0x2f, 0x61, 0x5a, 0x36, 0x7b, 0xc3, 0x45, 0xa7, 0x73, 0x7b, + 0x96, 0x26, 0x1d, 0x08, 0xf6, 0xff, 0x07, 0x4d, 0xed, 0x0f, 0x0b, 0x5e, 0x1b, 0x05, 0x5d, 0x42, + 0x37, 0x7f, 0x37, 0xd8, 0xcd, 0x1f, 0xcc, 0x9d, 0xe0, 0x84, 0xce, 0xfe, 0x39, 0x0f, 0xe5, 0x49, + 0xd5, 0x40, 0xf7, 0xfb, 0xcd, 0x62, 0xcd, 0x63, 0xe4, 0xd2, 0xd8, 0x46, 0x89, 0xc7, 0x35, 0xca, + 0x5c, 0xcd, 0x78, 0x6d, 0xda, 0x26, 0x41, 0x75, 0x58, 0x53, 0xe5, 0x6c, 0x10, 0x41, 0xf7, 0x43, + 0x21, 0x49, 0xe8, 0xd1, 0xa4, 0x37, 0xaf, 0xb8, 0x65, 0x53, 0x96, 0xb5, 0xfa, 0xd0, 0x3d, 0x1e, + 0x41, 0xd4, 0xfe, 0xb4, 0x60, 0x55, 0x15, 0x0b, 0x53, 0x9f, 0x5d, 0x9e, 0xc9, 0xfd, 0x21, 0x93, + 0x7f, 0x38, 0x83, 0x07, 0x32, 0x91, 0x3e, 0xc7, 0xe1, 0xe7, 0x16, 0xbc, 0x32, 0x84, 0xb8, 0x04, + 0x7b, 0x3f, 0x18, 0xb4, 0xf7, 0xad, 0xf9, 0x52, 0x9b, 0xe0, 0xed, 0x5f, 0x2c, 0xb8, 0x36, 0xb6, + 0x08, 0xa8, 0x02, 0x79, 0xe6, 0x9b, 0x21, 0x09, 0x06, 0x9b, 0xdf, 0xaf, 0xe3, 0x3c, 0xf3, 0xd1, + 0x5d, 0x28, 0xfa, 0x44, 0xa6, 0x03, 0xf2, 0xed, 0xe9, 0x92, 0xbd, 0xcf, 0x02, 0xda, 0x1f, 0xb7, + 0x75, 0xa2, 0xc6, 0xad, 0x62, 0x41, 0xef, 0xc0, 0x32, 0x1b, 0x34, 0x5c, 0xaf, 0x1c, 0x3d, 0xa3, + 0xf5, 0xbe, 0xa8, 0x09, 0x3d, 0xca, 0xd5, 0x68, 0x45, 0x6f, 0x42, 0xc1, 0x8b, 0xda, 0x26, 0xc8, + 0x92, 0x01, 0x15, 0x76, 0x0f, 0xbf, 0xc0, 0xea, 0x5c, 0x4d, 0x7a, 0x9f, 0x89, 0x96, 0xfe, 0x5d, + 0x93, 0x91, 0x66, 0xa2, 0x85, 0x93, 0x1b, 0xf4, 0x16, 0x2c, 0x06, 0x34, 0xe0, 0xf1, 0xa9, 0x11, + 0xee, 0xbd, 0xfc, 0xbd, 0xe4, 0x14, 0x9b, 0x5b, 0xf7, 0xd3, 0xb3, 0x8b, 0x6a, 0xee, 0xfc, 0xa2, + 0x9a, 0x7b, 0x7a, 0x51, 0xcd, 0x3d, 0xe9, 0x56, 0xad, 0xb3, 0x6e, 0xd5, 0x3a, 0xef, 0x56, 0xad, + 0xa7, 0xdd, 0xaa, 0xf5, 0x77, 0xb7, 0x6a, 0xfd, 0xf4, 0x4f, 0x35, 0xf7, 0xf5, 0x8d, 0xa9, 0xfe, + 0x31, 0xfc, 0x17, 0x00, 0x00, 0xff, 0xff, 0x40, 0xb1, 0x08, 0xfe, 0x63, 0x0c, 0x00, 0x00, } func (m *AppCat) Marshal() (dAtA []byte, err error) { @@ -760,6 +853,139 @@ func (m *VSHNPostgresBackupStatus) MarshalToSizedBuffer(dAtA []byte) (int, error return len(dAtA) - i, nil } +func (m *VSHNRedisBackup) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *VSHNRedisBackup) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *VSHNRedisBackup) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Status.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size, err := m.ObjectMeta.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *VSHNRedisBackupList) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *VSHNRedisBackupList) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *VSHNRedisBackupList) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Items) > 0 { + for iNdEx := len(m.Items) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Items[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + { + size, err := m.ListMeta.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *VSHNRedisBackupStatus) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *VSHNRedisBackupStatus) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *VSHNRedisBackupStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + i -= len(m.Instance) + copy(dAtA[i:], m.Instance) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Instance))) + i-- + dAtA[i] = 0x1a + { + size, err := m.Date.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + func (m *VSHNSize) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -944,6 +1170,51 @@ func (m *VSHNPostgresBackupStatus) Size() (n int) { return n } +func (m *VSHNRedisBackup) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.ObjectMeta.Size() + n += 1 + l + sovGenerated(uint64(l)) + l = m.Status.Size() + n += 1 + l + sovGenerated(uint64(l)) + return n +} + +func (m *VSHNRedisBackupList) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.ListMeta.Size() + n += 1 + l + sovGenerated(uint64(l)) + if len(m.Items) > 0 { + for _, e := range m.Items { + l = e.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + } + return n +} + +func (m *VSHNRedisBackupStatus) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ID) + n += 1 + l + sovGenerated(uint64(l)) + l = m.Date.Size() + n += 1 + l + sovGenerated(uint64(l)) + l = len(m.Instance) + n += 1 + l + sovGenerated(uint64(l)) + return n +} + func (m *VSHNSize) Size() (n int) { if m == nil { return 0 @@ -1086,6 +1357,45 @@ func (this *VSHNPostgresBackupStatus) String() string { }, "") return s } +func (this *VSHNRedisBackup) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&VSHNRedisBackup{`, + `ObjectMeta:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ObjectMeta), "ObjectMeta", "v1.ObjectMeta", 1), `&`, ``, 1) + `,`, + `Status:` + strings.Replace(strings.Replace(this.Status.String(), "VSHNRedisBackupStatus", "VSHNRedisBackupStatus", 1), `&`, ``, 1) + `,`, + `}`, + }, "") + return s +} +func (this *VSHNRedisBackupList) String() string { + if this == nil { + return "nil" + } + repeatedStringForItems := "[]VSHNRedisBackup{" + for _, f := range this.Items { + repeatedStringForItems += strings.Replace(strings.Replace(f.String(), "VSHNRedisBackup", "VSHNRedisBackup", 1), `&`, ``, 1) + "," + } + repeatedStringForItems += "}" + s := strings.Join([]string{`&VSHNRedisBackupList{`, + `ListMeta:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ListMeta), "ListMeta", "v1.ListMeta", 1), `&`, ``, 1) + `,`, + `Items:` + repeatedStringForItems + `,`, + `}`, + }, "") + return s +} +func (this *VSHNRedisBackupStatus) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&VSHNRedisBackupStatus{`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `Date:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Date), "Time", "v1.Time", 1), `&`, ``, 1) + `,`, + `Instance:` + fmt.Sprintf("%v", this.Instance) + `,`, + `}`, + }, "") + return s +} func (this *VSHNSize) String() string { if this == nil { return "nil" @@ -2328,6 +2638,386 @@ func (m *VSHNPostgresBackupStatus) Unmarshal(dAtA []byte) error { } return nil } +func (m *VSHNRedisBackup) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: VSHNRedisBackup: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: VSHNRedisBackup: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ObjectMeta", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ObjectMeta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Status.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *VSHNRedisBackupList) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: VSHNRedisBackupList: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: VSHNRedisBackupList: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ListMeta", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ListMeta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Items", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Items = append(m.Items, VSHNRedisBackup{}) + if err := m.Items[len(m.Items)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *VSHNRedisBackupStatus) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: VSHNRedisBackupStatus: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: VSHNRedisBackupStatus: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Date", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Date.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Instance", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Instance = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *VSHNSize) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/apis/appcat/v1/register.go b/apis/appcat/v1/register.go index 6afa53042..0dabe4c33 100644 --- a/apis/appcat/v1/register.go +++ b/apis/appcat/v1/register.go @@ -29,6 +29,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { &AppCatList{}, &VSHNPostgresBackup{}, &VSHNPostgresBackupList{}, + &VSHNRedisBackup{}, + &VSHNRedisBackupList{}, ) metav1.AddToGroupVersion(scheme, GroupVersion) return nil diff --git a/apis/appcat/v1/vshn_redis_backup_types.go b/apis/appcat/v1/vshn_redis_backup_types.go new file mode 100644 index 000000000..a482156fb --- /dev/null +++ b/apis/appcat/v1/vshn_redis_backup_types.go @@ -0,0 +1,72 @@ +package v1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/apiserver-runtime/pkg/builder/resource" +) + +// VSHNRedisBackup needs to implement the builder resource interface +var _ resource.Object = &VSHNRedisBackup{} +var _ resource.ObjectList = &VSHNRedisBackupList{} + +// +kubebuilder:object:root=true + +type VSHNRedisBackup struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Status VSHNRedisBackupStatus `json:"status,omitempty"` +} + +type VSHNRedisBackupStatus struct { + ID string `json:"id,omitempty"` + Date metav1.Time `json:"date,omitempty"` + Instance string `json:"instance,omitempty"` +} + +// +kubebuilder:object:root=true + +type VSHNRedisBackupList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + + Items []VSHNRedisBackup `json:"items,omitempty"` +} + +// GetGroupVersionResource returns the GroupVersionResource for this resource. +// The resource should be the all lowercase and pluralized kind +func (in *VSHNRedisBackup) GetGroupVersionResource() schema.GroupVersionResource { + return schema.GroupVersionResource{ + Group: GroupVersion.Group, + Version: GroupVersion.Version, + Resource: "vshnredisbackups", + } +} + +func (in *VSHNRedisBackup) GetObjectMeta() *metav1.ObjectMeta { + return &in.ObjectMeta +} + +// IsStorageVersion returns true if the object is also the internal version -- i.e. is the type defined for the API group or an alias to this object. +// If false, the resource is expected to implement MultiVersionObject interface. +func (in *VSHNRedisBackup) IsStorageVersion() bool { + return true +} + +func (in *VSHNRedisBackup) NamespaceScoped() bool { + return true +} + +func (in *VSHNRedisBackup) New() runtime.Object { + return &VSHNRedisBackup{} +} + +func (in *VSHNRedisBackup) NewList() runtime.Object { + return &VSHNRedisBackupList{} +} + +func (in *VSHNRedisBackupList) GetListMeta() *metav1.ListMeta { + return &in.ListMeta +} diff --git a/apis/appcat/v1/zz_generated.deepcopy.go b/apis/appcat/v1/zz_generated.deepcopy.go index 3be6781d2..3f57d9899 100644 --- a/apis/appcat/v1/zz_generated.deepcopy.go +++ b/apis/appcat/v1/zz_generated.deepcopy.go @@ -234,6 +234,80 @@ func (in *VSHNPostgresBackupStatus) DeepCopy() *VSHNPostgresBackupStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNRedisBackup) DeepCopyInto(out *VSHNRedisBackup) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNRedisBackup. +func (in *VSHNRedisBackup) DeepCopy() *VSHNRedisBackup { + if in == nil { + return nil + } + out := new(VSHNRedisBackup) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *VSHNRedisBackup) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNRedisBackupList) DeepCopyInto(out *VSHNRedisBackupList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]VSHNRedisBackup, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNRedisBackupList. +func (in *VSHNRedisBackupList) DeepCopy() *VSHNRedisBackupList { + if in == nil { + return nil + } + out := new(VSHNRedisBackupList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *VSHNRedisBackupList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNRedisBackupStatus) DeepCopyInto(out *VSHNRedisBackupStatus) { + *out = *in + in.Date.DeepCopyInto(&out.Date) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNRedisBackupStatus. +func (in *VSHNRedisBackupStatus) DeepCopy() *VSHNRedisBackupStatus { + if in == nil { + return nil + } + out := new(VSHNRedisBackupStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VSHNSize) DeepCopyInto(out *VSHNSize) { *out = *in diff --git a/apis/vshn/v1/dbaas_vshn_redis.go b/apis/vshn/v1/dbaas_vshn_redis.go index bfbb1cae8..f6ac7dd9b 100644 --- a/apis/vshn/v1/dbaas_vshn_redis.go +++ b/apis/vshn/v1/dbaas_vshn_redis.go @@ -27,6 +27,15 @@ type VSHNRedis struct { Status VSHNRedisStatus `json:"status,omitempty"` } +// +kubebuilder:object:generate=true +// +kubebuilder:object:root=true +type VSHNRedisList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + + Items []VSHNRedis `json:"items,omitempty"` +} + // VSHNRedisSpec defines the desired state of a VSHNRedis. type VSHNRedisSpec struct { // Parameters are the configurable fields of a VSHNRedis. diff --git a/apis/vshn/v1/groupversion_info.go b/apis/vshn/v1/groupversion_info.go index b452f1e4e..a574426ec 100644 --- a/apis/vshn/v1/groupversion_info.go +++ b/apis/vshn/v1/groupversion_info.go @@ -21,5 +21,12 @@ var ( ) func init() { - SchemeBuilder.Register(&VSHNPostgreSQL{}, &VSHNPostgreSQLList{}, &XVSHNPostgreSQL{}, &XVSHNPostgreSQLList{}) + SchemeBuilder.Register( + &VSHNPostgreSQL{}, + &VSHNPostgreSQLList{}, + &XVSHNPostgreSQL{}, + &XVSHNPostgreSQLList{}, + &VSHNRedis{}, + &VSHNRedisList{}, + ) } diff --git a/apis/vshn/v1/zz_generated.deepcopy.go b/apis/vshn/v1/zz_generated.deepcopy.go index dbfb66848..52395c57b 100644 --- a/apis/vshn/v1/zz_generated.deepcopy.go +++ b/apis/vshn/v1/zz_generated.deepcopy.go @@ -447,6 +447,38 @@ func (in *VSHNRedis) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNRedisList) DeepCopyInto(out *VSHNRedisList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]VSHNRedis, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNRedisList. +func (in *VSHNRedisList) DeepCopy() *VSHNRedisList { + if in == nil { + return nil + } + out := new(VSHNRedisList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *VSHNRedisList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VSHNRedisParameters) DeepCopyInto(out *VSHNRedisParameters) { *out = *in diff --git a/cmd/apiserver.go b/cmd/apiserver.go index 48262f419..8c521b0f7 100644 --- a/cmd/apiserver.go +++ b/cmd/apiserver.go @@ -3,12 +3,13 @@ package cmd import ( "log" "os" - "strconv" "github.com/spf13/cobra" + "github.com/spf13/viper" appcatv1 "github.com/vshn/appcat/apis/appcat/v1" "github.com/vshn/appcat/pkg/apiserver/appcat" "github.com/vshn/appcat/pkg/apiserver/vshn/postgres" + "github.com/vshn/appcat/pkg/apiserver/vshn/redis" "sigs.k8s.io/apiserver-runtime/pkg/builder" ) @@ -17,14 +18,22 @@ var apiServerCMDStr = "apiserver" var APIServerCMD = newAPIServerCMD() func newAPIServerCMD() *cobra.Command { - var appcatEnabled, vshnBackupsEnabled bool + + viper.AutomaticEnv() + + var appcatEnabled, vshnPGBackupsEnabled, vshnRedisBackupsEnabled bool if len(os.Args) < 2 { return &cobra.Command{} } if os.Args[1] == apiServerCMDStr { - appcatEnabled, vshnBackupsEnabled = parseEnvVariables() + appcatEnabled = viper.GetBool("APPCAT_HANDLER_ENABLED") + vshnPGBackupsEnabled = viper.GetBool("VSHN_POSTGRES_BACKUP_HANDLER_ENABLED") + vshnRedisBackupsEnabled = viper.GetBool("VSHN_REDIS_BACKUP_HANDLER_ENABLED") + if !appcatEnabled && !vshnPGBackupsEnabled && !vshnRedisBackupsEnabled { + log.Fatal("Handlers are not enabled, please set at least one of APPCAT_HANDLER_ENABLED | VSHN_POSTGRES_BACKUP_HANDLER_ENABLED | VSHN_REDIS_BACKUP_HANDLER_ENABLED env variables to True") + } } b := builder.APIServer @@ -33,10 +42,14 @@ func newAPIServerCMD() *cobra.Command { b.WithResourceAndHandler(&appcatv1.AppCat{}, appcat.New()) } - if vshnBackupsEnabled { + if vshnPGBackupsEnabled { b.WithResourceAndHandler(&appcatv1.VSHNPostgresBackup{}, postgres.New()) } + if vshnRedisBackupsEnabled { + b.WithResourceAndHandler(&appcatv1.VSHNRedisBackup{}, redis.New()) + } + b.WithoutEtcd(). ExposeLoopbackAuthorizer(). ExposeLoopbackMasterClientConfig() @@ -50,19 +63,3 @@ func newAPIServerCMD() *cobra.Command { } return cmd } - -func parseEnvVariables() (bool, bool) { - appcatEnabled, err := strconv.ParseBool(os.Getenv("APPCAT_HANDLER_ENABLED")) - if err != nil { - log.Fatal("Can't parse APPCAT_HANDLER_ENABLED env variable") - } - vshnBackupsEnabled, err := strconv.ParseBool(os.Getenv("VSHN_POSTGRES_BACKUP_HANDLER_ENABLED")) - if err != nil { - log.Fatal("Can't parse VSHN_POSTGRES_BACKUP_HANDLER_ENABLED env variable") - } - - if !appcatEnabled && !vshnBackupsEnabled { - log.Fatal("Handlers are not enabled, please set at least one of APPCAT_HANDLER_ENABLED | VSHN_POSTGRES_BACKUP_HANDLER_ENABLED env variables to True") - } - return appcatEnabled, vshnBackupsEnabled -} diff --git a/config/apiserver/role.yaml b/config/apiserver/role.yaml index b36843f5a..eb4d18649 100644 --- a/config/apiserver/role.yaml +++ b/config/apiserver/role.yaml @@ -66,6 +66,15 @@ rules: - vshn.appcat.vshn.io resources: - xvshnpostgresqls + - vshnredis + verbs: + - get + - list + - watch +- apiGroups: + - k8up.io + resources: + - snapshots verbs: - get - list diff --git a/go.mod b/go.mod index c1fe28556..049584580 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( github.com/gogo/protobuf v1.3.2 github.com/golang/mock v1.6.0 github.com/golang/protobuf v1.5.2 + github.com/google/go-containerregistry v0.9.0 github.com/hashicorp/go-version v1.6.0 github.com/jackc/pgx/v5 v5.3.1 github.com/k8up-io/k8up/v2 v2.7.1 @@ -34,6 +35,7 @@ require ( go.uber.org/zap v1.24.0 golang.org/x/text v0.9.0 google.golang.org/grpc v1.52.0 + gopkg.in/yaml.v3 v3.0.1 gotest.tools/v3 v3.0.3 k8s.io/api v0.26.3 k8s.io/apimachinery v0.26.3 @@ -176,7 +178,6 @@ require ( gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools v2.2.0+incompatible // indirect helm.sh/helm/v3 v3.10.2 // indirect k8s.io/apiextensions-apiserver v0.26.3 // indirect diff --git a/go.sum b/go.sum index 84a90bf18..cd1dbc180 100644 --- a/go.sum +++ b/go.sum @@ -271,6 +271,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-containerregistry v0.9.0 h1:5Ths7RjxyFV0huKChQTgY6fLzvHhZMpLTFNja8U0/0w= +github.com/google/go-containerregistry v0.9.0/go.mod h1:9eq4BnSufyT1kHNffX+vSXVonaJ7yaIOulrKZejMxnQ= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= diff --git a/pkg/apiserver/common.go b/pkg/apiserver/common.go index f561f89a6..c258d8552 100644 --- a/pkg/apiserver/common.go +++ b/pkg/apiserver/common.go @@ -2,8 +2,11 @@ package apiserver import ( "errors" + "sync" + apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/watch" ) func ResolveError(groupResource schema.GroupResource, err error) error { @@ -19,3 +22,48 @@ func ResolveError(groupResource schema.GroupResource, err error) error { } return err } + +// MultiWatch is wrapper of multiple source watches which implements the same methods as a normal watch.Watch +var _ watch.Interface = &MultiWatcher{} + +type MultiWatcher struct { + watchers []watch.Interface + eventChan chan watch.Event + wg sync.WaitGroup +} + +// NewEmptyMultiWatch creates an empty watch +func NewEmptyMultiWatch() *MultiWatcher { + return &MultiWatcher{ + eventChan: make(chan watch.Event), + } +} + +// AddWatcher adds a watch to this MultiWatcher +func (m *MultiWatcher) AddWatcher(w watch.Interface) { + m.watchers = append(m.watchers, w) +} + +// Stop stops all watches of this MultiWatch +func (m *MultiWatcher) Stop() { + for _, watcher := range m.watchers { + watcher.Stop() + } + m.wg.Wait() + close(m.eventChan) +} + +// ResultChan aggregates all channels from all watches of this MultiWatch +func (m *MultiWatcher) ResultChan() <-chan watch.Event { + for _, w := range m.watchers { + m.wg.Add(1) + watcher := w + go func() { + defer m.wg.Done() + for c := range watcher.ResultChan() { + m.eventChan <- c + } + }() + } + return m.eventChan +} diff --git a/pkg/apiserver/vshn/k8up/k8up.go b/pkg/apiserver/vshn/k8up/k8up.go new file mode 100644 index 000000000..d166b08d4 --- /dev/null +++ b/pkg/apiserver/vshn/k8up/k8up.go @@ -0,0 +1,99 @@ +package k8up + +import ( + "context" + "fmt" + + k8upv1 "github.com/k8up-io/k8up/v2/api/v1" + metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/watch" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +var ( + K8upGVR = schema.GroupVersionResource{ + Group: k8upv1.GroupVersion.Group, + Version: k8upv1.GroupVersion.Version, + Resource: "snapshots", + } +) + +var _ Snapshothandler = &ConcreteSnapshotHandler{} + +// Snapshothandler is an interface that handles listing and getting of k8up snapsthots. +type Snapshothandler interface { + Get(ctx context.Context, id, instanceNamespace string) (*k8upv1.Snapshot, error) + List(ctx context.Context, instanceNamespace string) (*k8upv1.SnapshotList, error) + Watch(ctx context.Context, namespace string, options *metainternalversion.ListOptions) (watch.Interface, error) + GetFromEvent(in watch.Event) (*k8upv1.Snapshot, error) +} + +// ConcreteSnapshotHandler implements Snapshothandler. +type ConcreteSnapshotHandler struct { + client client.WithWatch +} + +// Get returns the snapshot with the given ID. +func (c *ConcreteSnapshotHandler) Get(ctx context.Context, id, instanceNamespace string) (*k8upv1.Snapshot, error) { + + snapshot := &k8upv1.Snapshot{} + + err := c.client.Get(ctx, client.ObjectKey{Namespace: instanceNamespace, Name: id}, snapshot) + if err != nil { + return snapshot, err + } + + return snapshot, nil +} + +// List returns all snapshots in the given namespace. +func (c *ConcreteSnapshotHandler) List(ctx context.Context, instanceNamespace string) (*k8upv1.SnapshotList, error) { + + snapshots := &k8upv1.SnapshotList{} + + err := c.client.List(ctx, snapshots, &client.ListOptions{Namespace: instanceNamespace}) + if err != nil { + return nil, err + } + + return snapshots, nil +} + +// Watch returns a watcher for the given objects +func (c *ConcreteSnapshotHandler) Watch(ctx context.Context, namespace string, options *metainternalversion.ListOptions) (watch.Interface, error) { + + snapshots, err := c.List(ctx, namespace) + if err != nil { + return nil, err + } + + return c.client.Watch(ctx, snapshots) + +} + +// GetFromEvent resolves watch.Event into k8upv1.Snapshot +func (c *ConcreteSnapshotHandler) GetFromEvent(in watch.Event) (*k8upv1.Snapshot, error) { + snap, ok := in.Object.(*unstructured.Unstructured) + if !ok { + return nil, fmt.Errorf("cannot parse snapshot from watch event") + } + + snapshot := &k8upv1.Snapshot{} + + err := runtime.DefaultUnstructuredConverter.FromUnstructured(snap.Object, snapshot) + if err != nil { + return nil, err + } + + return snapshot, nil +} + +// New returns a new Snapshothandler implemented by ConcreteSnapshotHandler. +func New(client client.WithWatch) Snapshothandler { + return &ConcreteSnapshotHandler{ + client: client, + } +} diff --git a/pkg/apiserver/vshn/postgres/list.go b/pkg/apiserver/vshn/postgres/list.go index c059eef9c..4cff5ff62 100644 --- a/pkg/apiserver/vshn/postgres/list.go +++ b/pkg/apiserver/vshn/postgres/list.go @@ -3,7 +3,8 @@ package postgres import ( "context" "fmt" - "github.com/vshn/appcat/apis/appcat/v1" + + v1 "github.com/vshn/appcat/apis/appcat/v1" "github.com/vshn/appcat/pkg/apiserver" metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" "k8s.io/apimachinery/pkg/runtime" @@ -62,7 +63,7 @@ func (v *vshnPostgresBackupStorage) Watch(ctx context.Context, options *metainte return nil, fmt.Errorf("cannot list VSHNPostgreSQL instances") } - mw := NewEmptyMultiWatch() + mw := apiserver.NewEmptyMultiWatch() for _, value := range instances.Items { backupWatcher, err := v.sgbackups.WatchSGBackup(ctx, value.Status.InstanceNamespace, options) if err != nil { diff --git a/pkg/apiserver/vshn/postgres/multiwatch.go b/pkg/apiserver/vshn/postgres/multiwatch.go deleted file mode 100644 index a123de445..000000000 --- a/pkg/apiserver/vshn/postgres/multiwatch.go +++ /dev/null @@ -1,51 +0,0 @@ -package postgres - -import ( - "k8s.io/apimachinery/pkg/watch" - "sync" -) - -// MultiWatch is wrapper of multiple source watches which implements the same methods as a normal watch.Watch -var _ watch.Interface = &MultiWatcher{} - -type MultiWatcher struct { - watchers []watch.Interface - eventChan chan watch.Event - wg sync.WaitGroup -} - -// NewEmptyMultiWatch creates an empty watch -func NewEmptyMultiWatch() *MultiWatcher { - return &MultiWatcher{ - eventChan: make(chan watch.Event), - } -} - -// AddWatcher adds a watch to this MultiWatcher -func (m *MultiWatcher) AddWatcher(w watch.Interface) { - m.watchers = append(m.watchers, w) -} - -// Stop stops all watches of this MultiWatch -func (m *MultiWatcher) Stop() { - for _, watcher := range m.watchers { - watcher.Stop() - } - m.wg.Wait() - close(m.eventChan) -} - -// ResultChan aggregates all channels from all watches of this MultiWatch -func (m *MultiWatcher) ResultChan() <-chan watch.Event { - for _, w := range m.watchers { - m.wg.Add(1) - watcher := w - go func() { - defer m.wg.Done() - for c := range watcher.ResultChan() { - m.eventChan <- c - } - }() - } - return m.eventChan -} diff --git a/pkg/apiserver/vshn/redis/backup.go b/pkg/apiserver/vshn/redis/backup.go new file mode 100644 index 000000000..644f9ff31 --- /dev/null +++ b/pkg/apiserver/vshn/redis/backup.go @@ -0,0 +1,74 @@ +package redis + +import ( + appcatv1 "github.com/vshn/appcat/apis/appcat/v1" + "github.com/vshn/appcat/pkg" + "github.com/vshn/appcat/pkg/apiserver/vshn/k8up" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + genericregistry "k8s.io/apiserver/pkg/registry/generic" + "k8s.io/apiserver/pkg/registry/rest" + restbuilder "sigs.k8s.io/apiserver-runtime/pkg/builder/rest" + "sigs.k8s.io/apiserver-runtime/pkg/util/loopback" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +var _ rest.Scoper = &vshnRedisBackupStorage{} +var _ rest.Storage = &vshnRedisBackupStorage{} + +type vshnRedisBackupStorage struct { + snapshothandler k8up.Snapshothandler + vshnRedis vshnRedisProvider +} + +// New returns a new resthandler for Redis backups. +func New() restbuilder.ResourceHandlerProvider { + return func(s *runtime.Scheme, gasdf genericregistry.RESTOptionsGetter) (rest.Storage, error) { + c, err := client.NewWithWatch(loopback.GetLoopbackMasterClientConfig(), client.Options{}) + if err != nil { + return nil, err + } + + pkg.AddToScheme(c.Scheme()) + pkg.AddToScheme(s) + + return &vshnRedisBackupStorage{ + snapshothandler: k8up.New(c), + vshnRedis: &concreteRedisProvider{ + client: c, + }, + }, nil + } +} + +func (v vshnRedisBackupStorage) New() runtime.Object { + return &appcatv1.VSHNRedisBackup{} +} + +func (v vshnRedisBackupStorage) Destroy() {} + +func (v *vshnRedisBackupStorage) NamespaceScoped() bool { + return true +} + +func trimStringLength(in string) string { + length := len(in) + if length > 8 { + length = 8 + } + return in[:length] +} + +func deRefString(in *string) string { + if in == nil { + return "" + } + return *in +} + +func deRefMetaTime(in *metav1.Time) metav1.Time { + if in == nil { + return metav1.Now() + } + return *in +} diff --git a/pkg/apiserver/vshn/redis/common_test.go b/pkg/apiserver/vshn/redis/common_test.go new file mode 100644 index 000000000..68eb9da69 --- /dev/null +++ b/pkg/apiserver/vshn/redis/common_test.go @@ -0,0 +1,64 @@ +package redis + +import ( + "context" + + k8upv1 "github.com/k8up-io/k8up/v2/api/v1" + vshnv1 "github.com/vshn/appcat/apis/vshn/v1" + "github.com/vshn/appcat/pkg/apiserver/vshn/k8up" + metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" + "k8s.io/apimachinery/pkg/watch" +) + +var _ vshnRedisProvider = &mockprovider{} +var _ k8up.Snapshothandler = &mockhandler{} + +type mockprovider struct { + err error + instances *vshnv1.VSHNRedisList +} + +func (m *mockprovider) ListVSHNRedis(ctx context.Context, namespace string) (*vshnv1.VSHNRedisList, error) { + + instances := &vshnv1.VSHNRedisList{ + Items: []vshnv1.VSHNRedis{}, + } + + for _, instance := range m.instances.Items { + if instance.GetNamespace() == namespace { + instances.Items = append(instances.Items, instance) + } + } + + return instances, m.err +} + +type mockhandler struct { + snapshot *k8upv1.Snapshot + snapshots *k8upv1.SnapshotList +} + +func (m *mockhandler) Get(ctx context.Context, id, instanceNamespace string) (*k8upv1.Snapshot, error) { + return m.snapshot, nil +} + +func (m *mockhandler) List(ctx context.Context, instanceNamespace string) (*k8upv1.SnapshotList, error) { + + snapshots := &k8upv1.SnapshotList{ + Items: []k8upv1.Snapshot{}, + } + + for _, snap := range m.snapshots.Items { + if snap.GetNamespace() == instanceNamespace { + snapshots.Items = append(snapshots.Items, snap) + } + } + + return snapshots, nil +} +func (m *mockhandler) Watch(ctx context.Context, namespace string, options *metainternalversion.ListOptions) (watch.Interface, error) { + return nil, nil +} +func (m *mockhandler) GetFromEvent(in watch.Event) (*k8upv1.Snapshot, error) { + return nil, nil +} diff --git a/pkg/apiserver/vshn/redis/create.go b/pkg/apiserver/vshn/redis/create.go new file mode 100644 index 000000000..b64d8d088 --- /dev/null +++ b/pkg/apiserver/vshn/redis/create.go @@ -0,0 +1,16 @@ +package redis + +import ( + "context" + "fmt" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/registry/rest" +) + +var _ rest.Creater = &vshnRedisBackupStorage{} + +func (v vshnRedisBackupStorage) Create(_ context.Context, _ runtime.Object, _ rest.ValidateObjectFunc, _ *metav1.CreateOptions) (runtime.Object, error) { + return nil, fmt.Errorf("method not implemented") +} diff --git a/pkg/apiserver/vshn/redis/delete.go b/pkg/apiserver/vshn/redis/delete.go new file mode 100644 index 000000000..b9ae53ccd --- /dev/null +++ b/pkg/apiserver/vshn/redis/delete.go @@ -0,0 +1,26 @@ +package redis + +import ( + "context" + "fmt" + + v1 "github.com/vshn/appcat/apis/appcat/v1" + metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/registry/rest" +) + +var _ rest.GracefulDeleter = &vshnRedisBackupStorage{} +var _ rest.CollectionDeleter = &vshnRedisBackupStorage{} + +func (v vshnRedisBackupStorage) Delete(_ context.Context, name string, _ rest.ValidateObjectFunc, _ *metav1.DeleteOptions) (runtime.Object, bool, error) { + return &v1.VSHNPostgresBackup{}, false, fmt.Errorf("method not implemented") +} + +func (v *vshnRedisBackupStorage) DeleteCollection(ctx context.Context, _ rest.ValidateObjectFunc, _ *metav1.DeleteOptions, _ *metainternalversion.ListOptions) (runtime.Object, error) { + return &v1.VSHNPostgresBackupList{ + Items: []v1.VSHNPostgresBackup{}, + }, fmt.Errorf("method not implemented") +} diff --git a/pkg/apiserver/vshn/redis/get.go b/pkg/apiserver/vshn/redis/get.go new file mode 100644 index 000000000..de8948d3e --- /dev/null +++ b/pkg/apiserver/vshn/redis/get.go @@ -0,0 +1,55 @@ +package redis + +import ( + "context" + "fmt" + + appcatv1 "github.com/vshn/appcat/apis/appcat/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/apiserver/pkg/registry/rest" +) + +var _ rest.Getter = &vshnRedisBackupStorage{} + +func (v *vshnRedisBackupStorage) Get(ctx context.Context, name string, opts *metav1.GetOptions) (runtime.Object, error) { + + namespace, ok := request.NamespaceFrom(ctx) + if !ok { + return nil, fmt.Errorf("cannot get namespace from context") + } + + instances, err := v.vshnRedis.ListVSHNRedis(ctx, namespace) + if err != nil { + return nil, err + } + + redisSnap := &appcatv1.VSHNRedisBackup{} + + for _, instance := range instances.Items { + ins := instance.Status.InstanceNamespace + snap, err := v.snapshothandler.Get(ctx, name, ins) + if err != nil { + if apierrors.IsNotFound(err) { + continue + } + return nil, err + } + + backupMeta := snap.ObjectMeta + backupMeta.Namespace = instance.GetNamespace() + + redisSnap = &appcatv1.VSHNRedisBackup{ + ObjectMeta: backupMeta, + Status: appcatv1.VSHNRedisBackupStatus{ + ID: deRefString(snap.Spec.ID), + Date: deRefMetaTime(snap.Spec.Date), + Instance: instance.GetName(), + }, + } + } + + return redisSnap, nil +} diff --git a/pkg/apiserver/vshn/redis/get_test.go b/pkg/apiserver/vshn/redis/get_test.go new file mode 100644 index 000000000..4587761f9 --- /dev/null +++ b/pkg/apiserver/vshn/redis/get_test.go @@ -0,0 +1,110 @@ +package redis + +import ( + "context" + "testing" + "time" + + k8upv1 "github.com/k8up-io/k8up/v2/api/v1" + "github.com/stretchr/testify/assert" + appcatv1 "github.com/vshn/appcat/apis/appcat/v1" + vshnv1 "github.com/vshn/appcat/apis/vshn/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/utils/pointer" +) + +func Test_vshnRedisBackupStorage_Get(t *testing.T) { + tests := []struct { + name string + instanceName string + instances *vshnv1.VSHNRedisList + snapshot *k8upv1.Snapshot + wantDate metav1.Time + wantNs string + want runtime.Object + wantErr bool + }{ + { + name: "GivenExistingBackup_ThenExpectBackup", + instances: &vshnv1.VSHNRedisList{ + Items: []vshnv1.VSHNRedis{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "myinstance", + Namespace: "ns1", + }, + Status: vshnv1.VSHNRedisStatus{ + InstanceNamespace: "ins1", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "myinstance", + Namespace: "ns2", + }, + Status: vshnv1.VSHNRedisStatus{ + InstanceNamespace: "ins2", + }, + }, + }, + }, + snapshot: &k8upv1.Snapshot{ + ObjectMeta: metav1.ObjectMeta{ + Name: "myid", + Namespace: "ns1", + }, + Spec: k8upv1.SnapshotSpec{ + ID: pointer.String("myid"), + }, + }, + wantDate: metav1.Date(2023, time.April, 3, 13, 37, 0, 0, time.UTC), + wantNs: "ns1", + want: &appcatv1.VSHNRedisBackup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "myid", + Namespace: "ns1", + }, + Status: appcatv1.VSHNRedisBackupStatus{ + ID: "myid", + Instance: "myinstance", + Date: metav1.Date(2023, time.April, 3, 13, 37, 0, 0, time.UTC), + }, + }, + }, + { + name: "GivenNoBackup_ThenExpectEmptyObjects", + instances: &vshnv1.VSHNRedisList{}, + snapshot: nil, + want: &appcatv1.VSHNRedisBackup{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + ctx := request.WithNamespace(context.TODO(), tt.wantNs) + + if tt.snapshot != nil { + tt.snapshot.Spec.Date = &tt.wantDate + } + + v := &vshnRedisBackupStorage{ + vshnRedis: &mockprovider{ + instances: tt.instances, + }, + snapshothandler: &mockhandler{ + snapshot: tt.snapshot, + }, + } + got, err := v.Get(ctx, tt.instanceName, &metav1.GetOptions{}) + if (err != nil) != tt.wantErr { + t.Errorf("vshnRedisBackupStorage.Get() error = %v, wantErr %v", err, tt.wantErr) + return + } + + assert.Equal(t, tt.want, got) + + }) + } +} diff --git a/pkg/apiserver/vshn/redis/list.go b/pkg/apiserver/vshn/redis/list.go new file mode 100644 index 000000000..ab581b133 --- /dev/null +++ b/pkg/apiserver/vshn/redis/list.go @@ -0,0 +1,61 @@ +package redis + +import ( + "context" + "fmt" + + appcatv1 "github.com/vshn/appcat/apis/appcat/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/apiserver/pkg/registry/rest" +) + +var _ rest.Lister = &vshnRedisBackupStorage{} + +func (v *vshnRedisBackupStorage) List(ctx context.Context, options *metainternalversion.ListOptions) (runtime.Object, error) { + + namespace, ok := request.NamespaceFrom(ctx) + if !ok { + return nil, fmt.Errorf("cannot get namespace from resource") + } + + instances, err := v.vshnRedis.ListVSHNRedis(ctx, namespace) + if err != nil { + return nil, err + } + + redisSnapshots := &appcatv1.VSHNRedisBackupList{ + Items: []appcatv1.VSHNRedisBackup{}, + } + + for _, instance := range instances.Items { + snapshots, err := v.snapshothandler.List(ctx, instance.Status.InstanceNamespace) + if err != nil && !apierrors.IsNotFound(err) { + return nil, err + } + + for _, snap := range snapshots.Items { + + backupMeta := snap.ObjectMeta + backupMeta.Namespace = instance.GetNamespace() + + redisSnapshots.Items = append(redisSnapshots.Items, appcatv1.VSHNRedisBackup{ + ObjectMeta: backupMeta, + Status: appcatv1.VSHNRedisBackupStatus{ + ID: deRefString(snap.Spec.ID), + Date: deRefMetaTime(snap.Spec.Date), + Instance: instance.GetName(), + }, + }) + } + + } + + return redisSnapshots, nil +} + +func (v *vshnRedisBackupStorage) NewList() runtime.Object { + return &appcatv1.VSHNRedisBackupList{} +} diff --git a/pkg/apiserver/vshn/redis/list_test.go b/pkg/apiserver/vshn/redis/list_test.go new file mode 100644 index 000000000..ebaa05c19 --- /dev/null +++ b/pkg/apiserver/vshn/redis/list_test.go @@ -0,0 +1,154 @@ +package redis + +import ( + "context" + "testing" + "time" + + k8upv1 "github.com/k8up-io/k8up/v2/api/v1" + "github.com/stretchr/testify/assert" + appcatv1 "github.com/vshn/appcat/apis/appcat/v1" + vshnv1 "github.com/vshn/appcat/apis/vshn/v1" + "k8s.io/apimachinery/pkg/apis/meta/internalversion" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/utils/pointer" +) + +func Test_vshnRedisBackupStorage_List(t *testing.T) { + tests := []struct { + name string + instances *vshnv1.VSHNRedisList + snapshots *k8upv1.SnapshotList + wantNs string + want runtime.Object + wantDate metav1.Time + wantErr bool + }{ + { + name: "GivenAvailableBackups_ThenExpectBackupList", + instances: &vshnv1.VSHNRedisList{ + Items: []vshnv1.VSHNRedis{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "myinstance", + Namespace: "ns1", + }, + Status: vshnv1.VSHNRedisStatus{ + InstanceNamespace: "ins1", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "mysecondinstance", + Namespace: "ns1", + }, + Status: vshnv1.VSHNRedisStatus{ + InstanceNamespace: "ins2", + }, + }, + }, + }, + snapshots: &k8upv1.SnapshotList{ + Items: []k8upv1.Snapshot{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "snap1", + Namespace: "ins1", + }, + Spec: k8upv1.SnapshotSpec{ + ID: pointer.String("snap1"), + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "snap2", + Namespace: "ins2", + }, + Spec: k8upv1.SnapshotSpec{ + ID: pointer.String("snap2"), + }, + }, + }, + }, + wantNs: "ns1", + wantDate: metav1.Date(2023, time.April, 3, 13, 37, 0, 0, time.UTC), + want: &appcatv1.VSHNRedisBackupList{ + Items: []appcatv1.VSHNRedisBackup{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "snap1", + Namespace: "ns1", + }, + Status: appcatv1.VSHNRedisBackupStatus{ + ID: "snap1", + Instance: "myinstance", + Date: metav1.Date(2023, time.April, 3, 13, 37, 0, 0, time.UTC), + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "snap2", + Namespace: "ns1", + }, + Status: appcatv1.VSHNRedisBackupStatus{ + ID: "snap2", + Instance: "mysecondinstance", + Date: metav1.Date(2023, time.April, 3, 13, 37, 0, 0, time.UTC), + }, + }, + }, + }, + }, + { + name: "GivenNobackups_ThenExpectEmptyList", + instances: &vshnv1.VSHNRedisList{ + Items: []vshnv1.VSHNRedis{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "myinstance", + Namespace: "ns1", + }, + Status: vshnv1.VSHNRedisStatus{ + InstanceNamespace: "ins1", + }, + }, + }, + }, + snapshots: &k8upv1.SnapshotList{ + Items: []k8upv1.Snapshot{}, + }, + wantNs: "ns1", + want: &appcatv1.VSHNRedisBackupList{ + Items: []appcatv1.VSHNRedisBackup{}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + ctx := request.WithNamespace(context.TODO(), tt.wantNs) + + for i := range tt.snapshots.Items { + tt.snapshots.Items[i].Spec.Date = &tt.wantDate + } + + v := &vshnRedisBackupStorage{ + snapshothandler: &mockhandler{ + snapshots: tt.snapshots, + }, + vshnRedis: &mockprovider{ + instances: tt.instances, + }, + } + got, err := v.List(ctx, &internalversion.ListOptions{}) + if (err != nil) != tt.wantErr { + t.Errorf("vshnRedisBackupStorage.List() error = %v, wantErr %v", err, tt.wantErr) + return + } + + assert.Equal(t, tt.want, got) + }) + } +} diff --git a/pkg/apiserver/vshn/redis/table.go b/pkg/apiserver/vshn/redis/table.go new file mode 100644 index 000000000..0261a95f3 --- /dev/null +++ b/pkg/apiserver/vshn/redis/table.go @@ -0,0 +1,59 @@ +package redis + +import ( + "context" + "fmt" + + appcatv1 "github.com/vshn/appcat/apis/appcat/v1" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/registry/rest" +) + +var _ rest.TableConvertor = &vshnRedisBackupStorage{} + +func (v *vshnRedisBackupStorage) ConvertToTable(_ context.Context, obj runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) { + + table := &metav1.Table{} + + backups := []appcatv1.VSHNRedisBackup{} + if meta.IsListType(obj) { + backupList, ok := obj.(*appcatv1.VSHNRedisBackupList) + if !ok { + return nil, fmt.Errorf("not a vshn redis backup: %#v", obj) + } + backups = backupList.Items + } else { + backup, ok := obj.(*appcatv1.VSHNRedisBackup) + if !ok { + return nil, fmt.Errorf("not a vshn redis backup: %#v", obj) + } + backups = append(backups, *backup) + } + + for _, backup := range backups { + table.Rows = append(table.Rows, backupToTableRow(&backup)) + } + + if opt, ok := tableOptions.(*metav1.TableOptions); !ok || !opt.NoHeaders { + table.ColumnDefinitions = []metav1.TableColumnDefinition{ + {Name: "Backup ID", Type: "string", Format: "name", Description: "ID of the snapshot"}, + {Name: "Database Instance", Type: "string", Description: "The redis instance"}, + {Name: "Backup Time", Type: "string", Description: "When backup was made"}, + } + } + + return table, nil +} + +func backupToTableRow(backup *appcatv1.VSHNRedisBackup) metav1.TableRow { + + return metav1.TableRow{ + Cells: []interface{}{ + trimStringLength(backup.Status.ID), + backup.Status.Instance, + backup.Status.Date}, + Object: runtime.RawExtension{Object: backup}, + } +} diff --git a/pkg/apiserver/vshn/redis/table_test.go b/pkg/apiserver/vshn/redis/table_test.go new file mode 100644 index 000000000..ea8c000fe --- /dev/null +++ b/pkg/apiserver/vshn/redis/table_test.go @@ -0,0 +1,65 @@ +package redis + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + vshnv1 "github.com/vshn/appcat/apis/appcat/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +func Test_vshnRedisBackupStorage_ConvertToTable(t *testing.T) { + tests := []struct { + name string + obj runtime.Object + tableOptions runtime.Object + wantRows int + wantErr bool + }{ + { + name: "GivenOneBackup_ThenExpectOneRow", + obj: &vshnv1.VSHNRedisBackup{}, + wantRows: 1, + }, + { + name: "GivenMiltipleBackups_ThenExpectMultipleRows", + obj: &vshnv1.VSHNRedisBackupList{ + Items: []vshnv1.VSHNRedisBackup{ + {}, + {}, + {}, + }, + }, + wantRows: 3, + }, + { + name: "GivenNoBackupObject_ThenExpectError", + obj: &vshnv1.AppCat{}, + wantErr: true, + }, + { + name: "GivenNil_ThenExpectError", + obj: nil, + wantErr: true, + }, + { + name: "GivenNonBackupList_THenExpectError", + obj: &vshnv1.VSHNPostgresBackupList{}, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + v := &vshnRedisBackupStorage{} + got, err := v.ConvertToTable(context.TODO(), tt.obj, tt.tableOptions) + if tt.wantErr { + assert.Error(t, err) + return + } + assert.NoError(t, err) + + assert.Len(t, got.Rows, tt.wantRows) + }) + } +} diff --git a/pkg/apiserver/vshn/redis/update.go b/pkg/apiserver/vshn/redis/update.go new file mode 100644 index 000000000..34c24664b --- /dev/null +++ b/pkg/apiserver/vshn/redis/update.go @@ -0,0 +1,18 @@ +package redis + +import ( + "context" + "fmt" + + v1 "github.com/vshn/appcat/apis/appcat/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/registry/rest" +) + +var _ rest.Updater = &vshnRedisBackupStorage{} +var _ rest.CreaterUpdater = &vshnRedisBackupStorage{} + +func (v vshnRedisBackupStorage) Update(_ context.Context, name string, _ rest.UpdatedObjectInfo, _ rest.ValidateObjectFunc, _ rest.ValidateObjectUpdateFunc, _ bool, _ *metav1.UpdateOptions) (runtime.Object, bool, error) { + return &v1.VSHNPostgresBackup{}, false, fmt.Errorf("method not implemented") +} diff --git a/pkg/apiserver/vshn/redis/vshnredis.go b/pkg/apiserver/vshn/redis/vshnredis.go new file mode 100644 index 000000000..2e71594ea --- /dev/null +++ b/pkg/apiserver/vshn/redis/vshnredis.go @@ -0,0 +1,28 @@ +package redis + +import ( + "context" + + vshnv1 "github.com/vshn/appcat/apis/vshn/v1" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +type vshnRedisProvider interface { + ListVSHNRedis(ctx context.Context, namespace string) (*vshnv1.VSHNRedisList, error) +} + +type concreteRedisProvider struct { + client client.Client +} + +func (c *concreteRedisProvider) ListVSHNRedis(ctx context.Context, namespace string) (*vshnv1.VSHNRedisList, error) { + + instances := &vshnv1.VSHNRedisList{} + + err := c.client.List(ctx, instances, &client.ListOptions{Namespace: namespace}) + if err != nil { + return nil, err + } + + return instances, nil +} diff --git a/pkg/apiserver/vshn/redis/watch.go b/pkg/apiserver/vshn/redis/watch.go new file mode 100644 index 000000000..4e6c9e681 --- /dev/null +++ b/pkg/apiserver/vshn/redis/watch.go @@ -0,0 +1,76 @@ +package redis + +import ( + "context" + "fmt" + + appcatv1 "github.com/vshn/appcat/apis/appcat/v1" + v1 "github.com/vshn/appcat/apis/appcat/v1" + "github.com/vshn/appcat/pkg/apiserver" + metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/apiserver/pkg/registry/rest" +) + +var _ rest.Watcher = &vshnRedisBackupStorage{} + +func (v *vshnRedisBackupStorage) Watch(ctx context.Context, options *metainternalversion.ListOptions) (watch.Interface, error) { + namespace, ok := request.NamespaceFrom(ctx) + if !ok { + return nil, fmt.Errorf("cannot get namespace from resource") + } + + instances, err := v.vshnRedis.ListVSHNRedis(ctx, namespace) + if err != nil { + return nil, fmt.Errorf("cannot list VSHNPostgreSQL instances") + } + + mw := apiserver.NewEmptyMultiWatch() + for _, value := range instances.Items { + backupWatcher, err := v.snapshothandler.Watch(ctx, value.Status.InstanceNamespace, options) + if err != nil { + return nil, apiserver.ResolveError(v1.GetGroupResource(v1.ResourceBackup), err) + } + mw.AddWatcher(backupWatcher) + } + + return watch.Filter(mw, func(in watch.Event) (out watch.Event, keep bool) { + if in.Object == nil { + // This should never happen, let downstream deal with it + return in, true + } + + backupInfo, err := v.snapshothandler.GetFromEvent(in) + if err != nil { + return in, false + } + + db := "" + namespace := "" + for _, value := range instances.Items { + if value.Status.InstanceNamespace == backupInfo.GetNamespace() { + db = value.GetName() + namespace = value.GetNamespace() + } + } + + if db == "" { + return in, false + } + + backupMeta := backupInfo.ObjectMeta + backupMeta.Namespace = namespace + + in.Object = &appcatv1.VSHNRedisBackup{ + ObjectMeta: backupMeta, + Status: v1.VSHNRedisBackupStatus{ + ID: deRefString(backupInfo.Spec.ID), + Date: deRefMetaTime(backupInfo.Spec.Date), + Instance: db, + }, + } + + return in, true + }), nil +} diff --git a/pkg/scheme.go b/pkg/scheme.go index 3197fb7c9..a0b555b97 100644 --- a/pkg/scheme.go +++ b/pkg/scheme.go @@ -16,6 +16,11 @@ import ( func SetupScheme() *runtime.Scheme { s := runtime.NewScheme() + AddToScheme(s) + return s +} + +func AddToScheme(s *runtime.Scheme) { _ = corev1.SchemeBuilder.AddToScheme(s) _ = xkube.SchemeBuilder.AddToScheme(s) _ = vshnv1.SchemeBuilder.SchemeBuilder.AddToScheme(s) @@ -25,5 +30,4 @@ func SetupScheme() *runtime.Scheme { _ = batchv1.SchemeBuilder.AddToScheme(s) _ = k8upv1.SchemeBuilder.AddToScheme(s) _ = xhelm.SchemeBuilder.AddToScheme(s) - return s }