Skip to content
This repository has been archived by the owner on May 23, 2023. It is now read-only.

Commit

Permalink
Merge pull request #34 from vshn/fix/encrypted_pvc_bug
Browse files Browse the repository at this point in the history
Patch SgCluster object via composition function
  • Loading branch information
Kidswiss authored May 9, 2023
2 parents c382f5d + 4ddb4a8 commit 8a88ef8
Show file tree
Hide file tree
Showing 11 changed files with 230 additions and 40 deletions.
44 changes: 34 additions & 10 deletions functions/vshn-postgres-func/encrypted_pvc.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@ import (
"context"
"fmt"

"github.com/go-logr/logr"
"github.com/sethvargo/go-password/password"
controllerruntime "sigs.k8s.io/controller-runtime"

"github.com/vshn/appcat-comp-functions/runtime"

stackgresv1 "github.com/vshn/appcat-comp-functions/apis/stackgres/v1"
vshnv1 "github.com/vshn/component-appcat/apis/vshn/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/pointer"
)

// AddPvcSecret adds a secret for the encrypted PVC for the PostgreSQL instance.
Expand All @@ -35,21 +39,42 @@ func AddPvcSecret(ctx context.Context, iof *runtime.Runtime) runtime.Result {
return runtime.NewNormal()
}

if comp.Status.InstanceNamespace == "" {
return runtime.NewWarning(ctx, "Composite is missing instance namespace, skipping transformation")
log.Info("Adding secret to composite")

cluster := &stackgresv1.SGCluster{}
err = iof.Desired.GetFromObject(ctx, cluster, "cluster")
if err != nil {
return runtime.NewFatalErr(ctx, "Cannot get SgCluster object", err)
}

log.Info("Adding secret to composite")
cluster.Spec.Pods.PersistentVolume.StorageClass = pointer.String("ssd-encrypted")
err = iof.Desired.PutIntoObject(ctx, cluster, "cluster")

secret := &v1.Secret{}
if err != nil {
return runtime.NewFatalErr(ctx, "Cannot edit SGCluster object", err)
}

pods := cluster.Spec.Instances

for i := 0; i < pods; i++ {
result := writeLuksSecret(ctx, iof, log, comp, i)
if result != nil {
return result
}
}

return runtime.NewNormal()
}

func writeLuksSecret(ctx context.Context, iof *runtime.Runtime, log logr.Logger, comp *vshnv1.VSHNPostgreSQL, i int) runtime.Result {
// luksSecretResourceName is the resource name defined in the composition
// This name is different from metadata.name of the same resource
// The value is hardcoded in the composition for each resource and due to crossplane limitation
// it cannot be matched to the metadata.name
luksSecretResourceName := comp.Name + "-luks-key"
luksSecretResourceName := fmt.Sprintf("%s-luks-key-%d", comp.Name, i)

err = iof.Observed.GetFromObject(ctx, secret, luksSecretResourceName)
secret := &v1.Secret{}
err := iof.Observed.GetFromObject(ctx, secret, luksSecretResourceName)
luksKey := ""
if err == runtime.ErrNotFound {
log.Info("Secret does not exist yet. Creating...")
Expand All @@ -66,8 +91,8 @@ func AddPvcSecret(ctx context.Context, iof *runtime.Runtime) runtime.Result {

secret = &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("%s-data-%s-0-luks-key", comp.ObjectMeta.Labels["crossplane.io/composite"], comp.ObjectMeta.Labels["crossplane.io/composite"]),
Namespace: comp.Status.InstanceNamespace,
Name: fmt.Sprintf("%s-data-%s-%d-luks-key", comp.ObjectMeta.Labels["crossplane.io/composite"], comp.ObjectMeta.Labels["crossplane.io/composite"], i),
Namespace: getInstanceNamespace(comp),
},
Data: map[string][]byte{
"luksKey": []byte(luksKey),
Expand All @@ -77,6 +102,5 @@ func AddPvcSecret(ctx context.Context, iof *runtime.Runtime) runtime.Result {
if err != nil {
return runtime.NewFatalErr(ctx, "Cannot add luks secret object", err)
}

return runtime.NewNormal()
return nil
}
31 changes: 16 additions & 15 deletions functions/vshn-postgres-func/encrypted_pvc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,22 @@ import (
xkube "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1alpha1"
xfnv1alpha1 "github.com/crossplane/crossplane/apis/apiextensions/fn/io/v1alpha1"
"github.com/stretchr/testify/assert"
sgv1 "github.com/vshn/appcat-comp-functions/apis/stackgres/v1"
stackgresv1 "github.com/vshn/appcat-comp-functions/apis/stackgres/v1"
"github.com/vshn/appcat-comp-functions/runtime"
vshnv1 "github.com/vshn/component-appcat/apis/vshn/v1"
v1 "k8s.io/api/core/v1"
"k8s.io/utils/pointer"
"sigs.k8s.io/yaml"
)

func init() {
err := runtime.AddToScheme(sgv1.SchemeBuilder.SchemeBuilder)
if err != nil {
panic(err)
}
}

func TestNoEncryptedPVC(t *testing.T) {
ctx := context.Background()

Expand Down Expand Up @@ -66,15 +76,6 @@ func TestGivenEncrypedPvcThenExpectOutput(t *testing.T) {

ctx := context.Background()

t.Run("GivenEncryptionEnabledNoInstanceNamespace_ThenExpectWarningOutput", func(t *testing.T) {

iof := loadRuntimeFromFile(t, "enc_pvc/03-GivenEncryptionParamsNoInstanceNamespace.yaml")

r := AddPvcSecret(ctx, iof)

assert.Equal(t, runtime.NewWarning(ctx, "Composite is missing instance namespace, skipping transformation"), r)
})

t.Run("GivenEncryptionEnabled_ThenExpectOutput", func(t *testing.T) {

iof := loadRuntimeFromFile(t, "enc_pvc/03-GivenEncryptionParams.yaml")
Expand All @@ -87,15 +88,17 @@ func TestGivenEncrypedPvcThenExpectOutput(t *testing.T) {

assert.NoError(t, iof.Observed.GetComposite(ctx, comp))

resName := comp.Name + "-luks-key"
resName := comp.Name + "-luks-key-0"
kubeObject := &xkube.Object{}
assert.NoError(t, iof.Desired.Get(ctx, kubeObject, resName))

//resName := inComp.Name + "-luks-key"
//assert.NoError(t, iof.Desired.GetManagedResource(resName, kubeObject))
s := &v1.Secret{}
assert.NoError(t, yaml.Unmarshal(kubeObject.Spec.ForProvider.Manifest.Raw, s))
assert.NotEmpty(t, s.Data["luksKey"])

cluster := &stackgresv1.SGCluster{}
assert.NoError(t, iof.Desired.GetFromObject(ctx, cluster, "cluster"))
assert.Equal(t, pointer.String("ssd-encrypted"), cluster.Spec.Pods.PersistentVolume.StorageClass)
})

t.Run("GivenEncryptionEnabledExistingSecret_ThenExpectOutput", func(t *testing.T) {
Expand All @@ -110,12 +113,10 @@ func TestGivenEncrypedPvcThenExpectOutput(t *testing.T) {

assert.NoError(t, iof.Observed.GetComposite(ctx, comp))

resName := comp.Name + "-luks-key"
resName := comp.Name + "-luks-key-0"
kubeObject := &xkube.Object{}
assert.NoError(t, iof.Desired.Get(ctx, kubeObject, resName))

//resName := inComp.Name + "-luks-key"
//assert.NoError(t, iof.Desired.GetManagedResource(resName, kubeObject))
s := &v1.Secret{}
assert.NoError(t, yaml.Unmarshal(kubeObject.Spec.ForProvider.Manifest.Raw, s))
assert.NotEmpty(t, s.Data["luksKey"])
Expand Down
11 changes: 11 additions & 0 deletions functions/vshn-postgres-func/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package vshnpostgres

import (
"fmt"

vshnv1 "github.com/vshn/component-appcat/apis/vshn/v1"
)

func getInstanceNamespace(comp *vshnv1.VSHNPostgreSQL) string {
return fmt.Sprintf("vshn-postgresql-%s", comp.GetName())
}
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func (s *server) RunFunction(ctx context.Context, in *pb.RunFunctionRequest) (*p

func main() {
flag.StringVar(&AddressFlag, "socket", "@crossplane/fn/default.sock", "optional -> set where socket should be located")
flag.IntVar(&LogLevel, "loglevel", 1, "optional -> set log level [0,1]")
flag.IntVar(&LogLevel, "loglevel", 0, "optional -> set log level [0,1]")
flag.Parse()
logger, err := runtime.NewZapLogger(AI.AppName, AI.Version, LogLevel, true)
if err != nil {
Expand Down
34 changes: 25 additions & 9 deletions runtime/desired.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ import (
"context"
"encoding/json"
"fmt"
"reflect"

xkube "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1alpha1"
v1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
xfnv1alpha1 "github.com/crossplane/crossplane/apis/apiextensions/fn/io/v1alpha1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"reflect"
controllerruntime "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
)
Expand Down Expand Up @@ -185,30 +186,45 @@ func (d *DesiredResources) put(ctx context.Context, obj client.Object, resName s
}
}

log.V(1).Info("No desired kube object found, adding new one with resource", "object", obj, "kube object name", resName)
d.resources = append(d.resources, desiredResource(
xfnv1alpha1.DesiredResource{
dr := desiredResource{
DesiredResource: xfnv1alpha1.DesiredResource{
Name: resName,
Resource: runtime.RawExtension{
Raw: rawData,
},
},
))
}

log.V(1).Info("No desired kube object found, adding new one with resource", "object", obj, "kube object name", resName)
d.resources = append(d.resources, &dr)
return nil
}

// desiredResource is a wrapper around xfnv1alpha1.DesiredResource
// so we can satisfy the Resource interface.
type desiredResource xfnv1alpha1.DesiredResource
type desiredResource struct {
xfnv1alpha1.DesiredResource
}

func (d desiredResource) GetName() string {
func (d *desiredResource) GetName() string {
return d.Name
}

func (d desiredResource) GetRaw() []byte {
func (d *desiredResource) GetRaw() []byte {
return d.Resource.Raw
}

func (d desiredResource) SetRaw(raw []byte) {
func (d *desiredResource) SetRaw(raw []byte) {
d.Resource.Raw = raw
}

func (d *desiredResource) GetDesiredResource() xfnv1alpha1.DesiredResource {
return d.DesiredResource
}

func (d *desiredResource) GetObservedResource() xfnv1alpha1.ObservedResource {
return xfnv1alpha1.ObservedResource{
Name: d.Name,
Resource: d.Resource,
}
}
18 changes: 16 additions & 2 deletions runtime/observed.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import (
"context"
"encoding/json"
"fmt"
"reflect"

xkube "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1alpha1"
xfnv1alpha1 "github.com/crossplane/crossplane/apis/apiextensions/fn/io/v1alpha1"
"reflect"
controllerruntime "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
)
Expand Down Expand Up @@ -64,7 +65,9 @@ func (o *ObservedResources) fromKubeObject(ctx context.Context, kobj *xkube.Obje

// observedResource is a wrapper around xfnv1alpha1.ObservedResource
// so we can satisfy the Resource interface.
type observedResource xfnv1alpha1.ObservedResource
type observedResource struct {
xfnv1alpha1.ObservedResource
}

func (o observedResource) GetName() string {
return o.Name
Expand All @@ -77,3 +80,14 @@ func (o observedResource) GetRaw() []byte {
func (o observedResource) SetRaw(raw []byte) {
o.Resource.Raw = raw
}

func (o observedResource) GetDesiredResource() xfnv1alpha1.DesiredResource {
return xfnv1alpha1.DesiredResource{
Name: o.Name,
Resource: o.Resource,
}
}

func (o observedResource) GetObservedResource() xfnv1alpha1.ObservedResource {
return o.ObservedResource
}
2 changes: 1 addition & 1 deletion runtime/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func Exec(ctx context.Context, log logr.Logger, runtime *Runtime, transform Tran

runtime.io.Desired.Resources = make([]xfnv1alpha1.DesiredResource, len(runtime.Desired.resources))
for i, r := range runtime.Desired.resources {
runtime.io.Desired.Resources[i] = xfnv1alpha1.DesiredResource(r.(desiredResource))
runtime.io.Desired.Resources[i] = r.GetDesiredResource()
}

return nil
Expand Down
11 changes: 9 additions & 2 deletions runtime/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ type Resource interface {
GetName() string
GetRaw() []byte
SetRaw([]byte)
// Returns this resource as a DesiredResource.
// Depending on the concrete underlying type, not all fields are populated.
GetDesiredResource() xfnv1alpha1.DesiredResource
// Returns this resource as an ObservedResource.
// Depending on the concrete underlying type, not all fields are populated.
GetObservedResource() xfnv1alpha1.ObservedResource
}

// KeyFuncIO is the key to the context value where the functionIO pointer is stored
Expand Down Expand Up @@ -112,7 +118,8 @@ func desiredResources(dr []xfnv1alpha1.DesiredResource) *[]Resource {
resources := make([]Resource, len(dr))

for i := range dr {
resources[i] = desiredResource(dr[i])
r := desiredResource{DesiredResource: dr[i]}
resources[i] = &r
}

return &resources
Expand All @@ -122,7 +129,7 @@ func observedResources(or []xfnv1alpha1.ObservedResource) *[]Resource {
resources := make([]Resource, len(or))

for i := range or {
resources[i] = observedResource(or[i])
resources[i] = observedResource{ObservedResource: or[i]}
}

return &resources
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,42 @@ desired:
parameters: null
writeConnectionSecretToRef: {}
status: {}
resources:
- resource:
apiVersion: kubernetes.crossplane.io/v1alpha1
kind: Object
metadata: {}
spec:
forProvider:
manifest:
apiVersion: stackgres.io/v1
kind: SGCluster
metadata: {}
spec:
configurations:
backups:
- cronSchedule: ''
retention: 6
sgObjectStorage: ''
sgPostgresConfig: ''
instances: 1
nonProductionOptions:
enableSetPatroniCpuRequests: true
enableSetPatroniMemoryRequests: true
pods:
persistentVolume:
size: ''
postgres:
ssl:
certificateSecretKeySelector:
key: tls.crt
name: tls-certificate
enabled: true
privateKeySecretKeySelector:
key: tls.key
name: tls-certificate
version: ''
sgInstanceProfile: ''
providerConfigRef:
name: kubernetes
name: cluster
Loading

0 comments on commit 8a88ef8

Please sign in to comment.