Skip to content

Commit

Permalink
Support objects for user-provided services credentials
Browse files Browse the repository at this point in the history
Previously Korifi supported plain `map[string]string` for UPSI
credentials. This deviates from the CF API spec where credentials can be
arbitrary objects.

As credentials are stored in secrets which are glorified
`map[string][]byte` we had to change the way we store the credentials
object:
* We marshal the credentials object into a byte array and store it under
  the `crdentials` key in the secret. We no longer use a secret key per
  every credentials value. The `CFServiceInstance.Spec.SecretName`
  references that credentials secret.
* We introduce `CFServiceInstance.Status.Credentials` and
  `CFServiceInstance.Status.CredentialsObservedVersion` that aremanaged
  by the `CFServiceInstance` controller. Once the controller
  reconciles the service instance, it sets the secret name and its
  resource version into the new status fields.
* The CFServiceBindingController reconciles the CFServiceInstance
  credentials secret into the flat format that is then used for the
  servicebinding.io binding. In case of a json object value to a key,
  the value is represented as a json string.
* Existing bindings are not affected when upgrading Korifi. However,
  once the user updates the service instance credentials, the binding
  secret is rebuilt which causes statefulset restart. Further updates to
  the credentials will not cause additional restarts.
* We have created ADR 16 documenting how this works

fixes cloudfoundry#2900

Co-authored-by: Danail Branekov <danailster@gmail.com>
  • Loading branch information
2 people authored and marsteg committed Mar 19, 2024
1 parent de20204 commit c8a2bcb
Show file tree
Hide file tree
Showing 24 changed files with 1,323 additions and 525 deletions.
4 changes: 2 additions & 2 deletions api/handlers/service_instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ var _ = Describe("ServiceInstance", func() {
requestValidator.DecodeAndValidateJSONPayloadStub = decodeAndValidatePayloadStub(&payloads.ServiceInstancePatch{
Name: tools.PtrTo("new-name"),
Tags: &[]string{"alice", "bob"},
Credentials: &map[string]string{"foo": "bar"},
Credentials: &map[string]any{"foo": "bar"},
Metadata: payloads.MetadataPatch{
Annotations: map[string]*string{"ann2": tools.PtrTo("ann_val2")},
Labels: map[string]*string{"lab2": tools.PtrTo("lab_val2")},
Expand Down Expand Up @@ -306,7 +306,7 @@ var _ = Describe("ServiceInstance", func() {
GUID: "service-instance-guid",
SpaceGUID: "space-guid",
Name: tools.PtrTo("new-name"),
Credentials: &map[string]string{"foo": "bar"},
Credentials: &map[string]any{"foo": "bar"},
Tags: &[]string{"alice", "bob"},
MetadataPatch: repositories.MetadataPatch{
Annotations: map[string]*string{"ann2": tools.PtrTo("ann_val2")},
Expand Down
15 changes: 8 additions & 7 deletions api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
)

var createTimeout = time.Second * 120
var conditionTimeout = time.Second * 120

func init() {
utilruntime.Must(korifiv1alpha1.AddToScheme(scheme.Scheme))
Expand Down Expand Up @@ -126,14 +126,14 @@ func main() {
privilegedCRClient,
userClientFactory,
nsPermissions,
conditions.NewConditionAwaiter[*korifiv1alpha1.CFOrg, korifiv1alpha1.CFOrgList](createTimeout),
conditions.NewConditionAwaiter[*korifiv1alpha1.CFOrg, korifiv1alpha1.CFOrgList](conditionTimeout),
)
spaceRepo := repositories.NewSpaceRepo(
namespaceRetriever,
orgRepo,
userClientFactory,
nsPermissions,
conditions.NewConditionAwaiter[*korifiv1alpha1.CFSpace, korifiv1alpha1.CFSpaceList](createTimeout),
conditions.NewConditionAwaiter[*korifiv1alpha1.CFSpace, korifiv1alpha1.CFSpaceList](conditionTimeout),
)
processRepo := repositories.NewProcessRepo(
namespaceRetriever,
Expand All @@ -147,7 +147,7 @@ func main() {
namespaceRetriever,
userClientFactory,
nsPermissions,
conditions.NewConditionAwaiter[*korifiv1alpha1.CFApp, korifiv1alpha1.CFAppList](createTimeout),
conditions.NewConditionAwaiter[*korifiv1alpha1.CFApp, korifiv1alpha1.CFAppList](conditionTimeout),
)
dropletRepo := repositories.NewDropletRepo(
userClientFactory,
Expand Down Expand Up @@ -183,18 +183,19 @@ func main() {
nsPermissions,
toolsregistry.NewRepositoryCreator(cfg.ContainerRegistryType),
cfg.ContainerRepositoryPrefix,
conditions.NewConditionAwaiter[*korifiv1alpha1.CFPackage, korifiv1alpha1.CFPackageList](createTimeout),
conditions.NewConditionAwaiter[*korifiv1alpha1.CFPackage, korifiv1alpha1.CFPackageList](conditionTimeout),
)
serviceInstanceRepo := repositories.NewServiceInstanceRepo(
namespaceRetriever,
userClientFactory,
nsPermissions,
conditions.NewConditionAwaiter[*korifiv1alpha1.CFServiceInstance, korifiv1alpha1.CFServiceInstanceList](conditionTimeout),
)
serviceBindingRepo := repositories.NewServiceBindingRepo(
namespaceRetriever,
userClientFactory,
nsPermissions,
conditions.NewConditionAwaiter[*korifiv1alpha1.CFServiceBinding, korifiv1alpha1.CFServiceBindingList](createTimeout),
conditions.NewConditionAwaiter[*korifiv1alpha1.CFServiceBinding, korifiv1alpha1.CFServiceBindingList](conditionTimeout),
)
buildpackRepo := repositories.NewBuildpackRepository(cfg.BuilderName,
userClientFactory,
Expand All @@ -221,7 +222,7 @@ func main() {
userClientFactory,
namespaceRetriever,
nsPermissions,
conditions.NewConditionAwaiter[*korifiv1alpha1.CFTask, korifiv1alpha1.CFTaskList](createTimeout),
conditions.NewConditionAwaiter[*korifiv1alpha1.CFTask, korifiv1alpha1.CFTaskList](conditionTimeout),
)
metricsRepo := repositories.NewMetricsRepo(userClientFactory)

Expand Down
12 changes: 6 additions & 6 deletions api/payloads/service_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type ServiceInstanceCreate struct {
Name string `json:"name"`
Type string `json:"type"`
Tags []string `json:"tags"`
Credentials map[string]string `json:"credentials"`
Credentials map[string]any `json:"credentials"`
Relationships *ServiceInstanceRelationships `json:"relationships"`
Metadata Metadata `json:"metadata"`
}
Expand Down Expand Up @@ -74,10 +74,10 @@ func (r ServiceInstanceRelationships) Validate() error {
}

type ServiceInstancePatch struct {
Name *string `json:"name,omitempty"`
Tags *[]string `json:"tags,omitempty"`
Credentials *map[string]string `json:"credentials,omitempty"`
Metadata MetadataPatch `json:"metadata"`
Name *string `json:"name,omitempty"`
Tags *[]string `json:"tags,omitempty"`
Credentials *map[string]any `json:"credentials,omitempty"`
Metadata MetadataPatch `json:"metadata"`
}

func (p ServiceInstancePatch) Validate() error {
Expand Down Expand Up @@ -120,7 +120,7 @@ func (p *ServiceInstancePatch) UnmarshalJSON(data []byte) error {
}

if v, ok := patchMap["credentials"]; ok && v == nil {
patch.Credentials = &map[string]string{}
patch.Credentials = &map[string]any{}
}

*p = ServiceInstancePatch(patch)
Expand Down
27 changes: 18 additions & 9 deletions api/payloads/service_instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,12 @@ var _ = Describe("ServiceInstanceCreate", func() {
Name: "service-instance-name",
Type: "user-provided",
Tags: []string{"foo", "bar"},
Credentials: map[string]string{
Credentials: map[string]any{
"username": "bob",
"password": "float",
"object": map[string]any{
"a": "b",
},
},
Relationships: &payloads.ServiceInstanceRelationships{
Space: &payloads.Relationship{
Expand Down Expand Up @@ -188,9 +191,13 @@ var _ = Describe("ServiceInstanceCreate", func() {
Expect(msg.Annotations).To(HaveKeyWithValue("ann1", "val_ann1"))
Expect(msg.Labels).To(HaveLen(1))
Expect(msg.Labels).To(HaveKeyWithValue("lab1", "val_lab1"))
Expect(msg.Credentials).To(HaveLen(2))
Expect(msg.Credentials).To(HaveKeyWithValue("username", "bob"))
Expect(msg.Credentials).To(HaveKeyWithValue("password", "float"))
Expect(msg.Credentials).To(MatchAllKeys(Keys{
"username": Equal("bob"),
"password": Equal("float"),
"object": MatchAllKeys(Keys{
"a": Equal("b"),
}),
}))
})
})
})
Expand Down Expand Up @@ -261,9 +268,10 @@ var _ = Describe("ServiceInstancePatch", func() {
patchPayload = payloads.ServiceInstancePatch{
Name: tools.PtrTo("service-instance-name"),
Tags: &[]string{"foo", "bar"},
Credentials: &map[string]string{
"username": "bob",
"password": "float",
Credentials: &map[string]any{
"object": map[string]any{
"a": "b",
},
},
Metadata: payloads.MetadataPatch{
Annotations: map[string]*string{"ann1": tools.PtrTo("val_ann1")},
Expand Down Expand Up @@ -316,8 +324,9 @@ var _ = Describe("ServiceInstancePatch", func() {
"lab1": PointTo(Equal("val_lab1")),
}))
Expect(msg.Credentials).To(PointTo(MatchAllKeys(Keys{
"username": Equal("bob"),
"password": Equal("float"),
"object": MatchAllKeys(Keys{
"a": Equal("b"),
}),
})))
})
})
Expand Down
Loading

0 comments on commit c8a2bcb

Please sign in to comment.