Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 0 additions & 14 deletions api/payloads/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,20 +313,6 @@ var _ = Describe("App payload validation", func() {
Expect(decodedPayload).To(gstruct.PointTo(Equal(payload)))
})

When("metadata is invalid", func() {
BeforeEach(func() {
payload.Metadata = payloads.MetadataPatch{
Labels: map[string]*string{
"foo.cloudfoundry.org/bar": tools.PtrTo("jim"),
},
}
})

It("returns an appropriate error", func() {
expectUnprocessableEntityError(validatorErr, "label/annotation key cannot use the cloudfoundry.org domain")
})
})

When("name is invalid", func() {
BeforeEach(func() {
payload.Name = "!@#"
Expand Down
14 changes: 0 additions & 14 deletions api/payloads/domain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,20 +165,6 @@ var _ = Describe("DomainUpdate", func() {
Expect(validatorErr).NotTo(HaveOccurred())
Expect(decodedUpdatePayload).To(gstruct.PointTo(Equal(updatePayload)))
})

When("metadata is invalid", func() {
BeforeEach(func() {
updatePayload.Metadata = payloads.MetadataPatch{
Labels: map[string]*string{
"foo.cloudfoundry.org/bar": tools.PtrTo("jim"),
},
}
})

It("returns an appropriate error", func() {
expectUnprocessableEntityError(validatorErr, "cannot use the cloudfoundry.org domain")
})
})
})

var _ = Describe("DomainList", func() {
Expand Down
28 changes: 0 additions & 28 deletions api/payloads/droplet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,33 +94,5 @@ var _ = Describe("DropletUpdate", func() {
Expect(validatorErr).NotTo(HaveOccurred())
Expect(decodedDropletPayload).To(gstruct.PointTo(Equal(updatePayload)))
})

When("metadata.labels contains an invalid key", func() {
BeforeEach(func() {
updatePayload.Metadata = payloads.MetadataPatch{
Labels: map[string]*string{
"foo.cloudfoundry.org/bar": tools.PtrTo("jim"),
},
}
})

It("returns an appropriate error", func() {
expectUnprocessableEntityError(validatorErr, "cannot use the cloudfoundry.org domain")
})
})

When("metadata.annotations contains an invalid key", func() {
BeforeEach(func() {
updatePayload.Metadata = payloads.MetadataPatch{
Annotations: map[string]*string{
"foo.cloudfoundry.org/bar": tools.PtrTo("jim"),
},
}
})

It("returns an appropriate error", func() {
expectUnprocessableEntityError(validatorErr, "cannot use the cloudfoundry.org domain")
})
})
})
})
34 changes: 3 additions & 31 deletions api/payloads/metadata.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
package payloads

import (
"errors"
"fmt"
"net/url"
"strings"

"code.cloudfoundry.org/korifi/api/tools/metadata"
"github.com/jellydator/validation"
)

Expand All @@ -28,36 +24,12 @@ type Metadata struct {

func (m Metadata) Validate() error {
return validation.ValidateStruct(&m,
validation.Field(&m.Annotations, validation.Map().Keys(validation.By(cloudfoundryKeyCheck)).AllowExtraKeys()),
validation.Field(&m.Labels, validation.Map().Keys(validation.By(cloudfoundryKeyCheck)).AllowExtraKeys()),
validation.Field(&m.Annotations, validation.Map().Keys(validation.By(metadata.CloudfoundryKeyCheck)).AllowExtraKeys()),
validation.Field(&m.Labels, validation.Map().Keys(validation.By(metadata.CloudfoundryKeyCheck)).AllowExtraKeys()),
)
}

type MetadataPatch struct {
Annotations map[string]*string `json:"annotations,omitempty"`
Labels map[string]*string `json:"labels,omitempty"`
}

func (p MetadataPatch) Validate() error {
return validation.ValidateStruct(&p,
validation.Field(&p.Annotations, validation.Map().Keys(validation.By(cloudfoundryKeyCheck)).AllowExtraKeys()),
validation.Field(&p.Labels, validation.Map().Keys(validation.By(cloudfoundryKeyCheck)).AllowExtraKeys()),
)
}

func cloudfoundryKeyCheck(key any) error {
keyStr, ok := key.(string)
if !ok {
return fmt.Errorf("expected string key, got %T", key)
}

u, err := url.ParseRequestURI("https://" + keyStr) // without the scheme, the hostname will be parsed as a path
if err != nil {
return nil
}

if strings.HasSuffix(u.Hostname(), "cloudfoundry.org") {
return errors.New("label/annotation key cannot use the cloudfoundry.org domain")
}
return nil
}
20 changes: 0 additions & 20 deletions api/payloads/metadata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,24 +84,4 @@ var _ = Describe("MetadataPatch", func() {
Expect(validatorErr).NotTo(HaveOccurred())
Expect(decodedMetadataPatchPayload).To(gstruct.PointTo(Equal(metadataPatchPayload)))
})

When("metadata.labels contains an invalid key", func() {
BeforeEach(func() {
metadataPatchPayload.Labels["foo.cloudfoundry.org/bar"] = tools.PtrTo("jim")
})

It("returns an appropriate error", func() {
expectUnprocessableEntityError(validatorErr, "cannot use the cloudfoundry.org domain")
})
})

When("metadata.annotations contains an invalid key", func() {
BeforeEach(func() {
metadataPatchPayload.Annotations["foo.cloudfoundry.org/bar"] = tools.PtrTo("jim")
})

It("returns an appropriate error", func() {
expectUnprocessableEntityError(validatorErr, "cannot use the cloudfoundry.org domain")
})
})
})
5 changes: 3 additions & 2 deletions api/payloads/org.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ func (p OrgCreate) ToMessage() repositories.CreateOrgMessage {
}

type OrgPatch struct {
Name *string `json:"name"`
Metadata MetadataPatch `json:"metadata"`
Name *string `json:"name"`
Suspended *bool `json:"suspended"`
Metadata MetadataPatch `json:"metadata"`
}

func (p OrgPatch) Validate() error {
Expand Down
12 changes: 0 additions & 12 deletions api/payloads/org_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,18 +86,6 @@ var _ = Describe("Org", func() {
Expect(decodedPayload).To(gstruct.PointTo(Equal(payload)))
})

When("the metadata is invalid", func() {
BeforeEach(func() {
payload.Metadata.Labels["cloudfoundry.org/test"] = tools.PtrTo("production")
})

It("returns an unprocessable entity error", func() {
expectUnprocessableEntityError(
validatorErr,
"label/annotation key cannot use the cloudfoundry.org domain",
)
})
})
When("the name is invalid", func() {
BeforeEach(func() {
payload.Name = tools.PtrTo("")
Expand Down
14 changes: 0 additions & 14 deletions api/payloads/package_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,20 +282,6 @@ var _ = Describe("PackageUpdate", func() {
Expect(validatorErr).NotTo(HaveOccurred())
Expect(decodedPayload).To(gstruct.PointTo(Equal(payload)))
})

When("metadata is invalid", func() {
BeforeEach(func() {
payload.Metadata = payloads.MetadataPatch{
Labels: map[string]*string{
"foo.cloudfoundry.org/bar": tools.PtrTo("jim"),
},
}
})

It("returns an appropriate error", func() {
expectUnprocessableEntityError(validatorErr, "label/annotation key cannot use the cloudfoundry.org domain")
})
})
})

Describe("ToMessage", func() {
Expand Down
13 changes: 0 additions & 13 deletions api/payloads/route_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,6 @@ var _ = Describe("RoutePatch", func() {
patchPayload payloads.RoutePatch
routePatch *payloads.RoutePatch
validatorErr error
apiError errors.ApiError
)

BeforeEach(func() {
Expand All @@ -203,24 +202,12 @@ var _ = Describe("RoutePatch", func() {

JustBeforeEach(func() {
validatorErr = validator.DecodeAndValidateJSONPayload(createJSONRequest(patchPayload), routePatch)
apiError, _ = validatorErr.(errors.ApiError)
})

It("succeeds", func() {
Expect(validatorErr).NotTo(HaveOccurred())
Expect(routePatch).To(gstruct.PointTo(Equal(patchPayload)))
})

When("metadata uses the cloudfoundry domain", func() {
BeforeEach(func() {
patchPayload.Metadata.Labels["foo.cloudfoundry.org/bar"] = tools.PtrTo("baz")
})

It("fails", func() {
Expect(apiError).To(HaveOccurred())
Expect(apiError.Detail()).To(ContainSubstring("cannot use the cloudfoundry.org domain"))
})
})
})

var _ = Describe("Add destination", func() {
Expand Down
13 changes: 0 additions & 13 deletions api/payloads/service_binding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,6 @@ var _ = Describe("ServiceBindingUpdate", func() {
patchPayload payloads.ServiceBindingUpdate
serviceBindingPatch *payloads.ServiceBindingUpdate
validatorErr error
apiError errors.ApiError
)

BeforeEach(func() {
Expand All @@ -272,22 +271,10 @@ var _ = Describe("ServiceBindingUpdate", func() {

JustBeforeEach(func() {
validatorErr = validator.DecodeAndValidateJSONPayload(createJSONRequest(patchPayload), serviceBindingPatch)
apiError, _ = validatorErr.(errors.ApiError)
})

It("succeeds", func() {
Expect(validatorErr).NotTo(HaveOccurred())
Expect(serviceBindingPatch).To(PointTo(Equal(patchPayload)))
})

When("metadata uses the cloudfoundry domain", func() {
BeforeEach(func() {
patchPayload.Metadata.Labels["foo.cloudfoundry.org/bar"] = tools.PtrTo("baz")
})

It("fails", func() {
Expect(apiError).To(HaveOccurred())
Expect(apiError.Detail()).To(ContainSubstring("cannot use the cloudfoundry.org domain"))
})
})
})
10 changes: 0 additions & 10 deletions api/payloads/service_instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -521,16 +521,6 @@ var _ = Describe("ServiceInstancePatch", func() {
})
})

When("metadata is invalid", func() {
BeforeEach(func() {
patchPayload.Metadata.Labels["foo.cloudfoundry.org/bar"] = tools.PtrTo("baz")
})

It("returns an appropriate error", func() {
expectUnprocessableEntityError(validatorErr, "label/annotation key cannot use the cloudfoundry.org domain")
})
})

Context("ToServiceInstancePatchMessage", func() {
It("converts to repo message correctly", func() {
msg := serviceInstancePatch.ToServiceInstancePatchMessage("space-guid", "app-guid")
Expand Down
12 changes: 0 additions & 12 deletions api/payloads/space_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,18 +126,6 @@ var _ = Describe("Space", func() {
Expect(decodedPayload).To(gstruct.PointTo(Equal(payload)))
})

When("the metadata is invalid", func() {
BeforeEach(func() {
payload.Metadata.Labels["cloudfoundry.org/test"] = tools.PtrTo("production")
})

It("returns an unprocessable entity error", func() {
expectUnprocessableEntityError(
validatorErr,
"label/annotation key cannot use the cloudfoundry.org domain",
)
})
})
When("the name is invalid", func() {
BeforeEach(func() {
payload.Name = tools.PtrTo("")
Expand Down
14 changes: 0 additions & 14 deletions api/payloads/task_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,20 +216,6 @@ var _ = Describe("TaskUpdate", func() {
Expect(validatorErr).NotTo(HaveOccurred())
Expect(decodedPayload).To(gstruct.PointTo(Equal(payload)))
})

When("metadata is invalid", func() {
BeforeEach(func() {
payload.Metadata = payloads.MetadataPatch{
Labels: map[string]*string{
"foo.cloudfoundry.org/bar": tools.PtrTo("jim"),
},
}
})

It("returns an appropriate error", func() {
expectUnprocessableEntityError(validatorErr, "label/annotation key cannot use the cloudfoundry.org domain")
})
})
})

Describe("ToMessage()", func() {
Expand Down
7 changes: 3 additions & 4 deletions api/repositories/app_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,8 +249,7 @@ func (f *AppRepo) PatchApp(ctx context.Context, authInfo authorization.Info, app
}

err := GetAndPatch(ctx, f.klient, cfApp, func() error {
appPatchMessage.Apply(cfApp)
return nil
return appPatchMessage.Apply(cfApp)
})
if err != nil {
return AppRecord{}, apierrors.FromK8sError(err, AppResourceType)
Expand Down Expand Up @@ -554,7 +553,7 @@ func (m *CreateAppMessage) toCFApp() korifiv1alpha1.CFApp {
}
}

func (m *PatchAppMessage) Apply(app *korifiv1alpha1.CFApp) {
func (m *PatchAppMessage) Apply(app *korifiv1alpha1.CFApp) error {
if m.Name != "" {
app.Spec.DisplayName = m.Name
}
Expand All @@ -573,7 +572,7 @@ func (m *PatchAppMessage) Apply(app *korifiv1alpha1.CFApp) {
}
}

m.MetadataPatch.Apply(app)
return m.MetadataPatch.Apply(app)
}

func cfAppToAppRecord(cfApp korifiv1alpha1.CFApp) (AppRecord, error) {
Expand Down
15 changes: 15 additions & 0 deletions api/repositories/app_repository_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,21 @@ var _ = Describe("AppRepository", func() {
createRoleBinding(ctx, userName, spaceDeveloperRole.Name, cfSpace.Name)
})

When("a label is invalid", func() {
BeforeEach(func() {
appPatchMessage.MetadataPatch.Labels["foo.cloudfoundry.org/bar"] = tools.PtrTo("baz")
})

It("returns an UnprocessableEntityError", func() {
var unprocessableEntityError apierrors.UnprocessableEntityError
Expect(errors.As(patchErr, &unprocessableEntityError)).To(BeTrue())
Expect(unprocessableEntityError.Detail()).To(SatisfyAll(
ContainSubstring("invalid labels patch"),
ContainSubstring(`"foo.cloudfoundry.org/bar"`),
))
})
})

It("updates the app", func() {
Expect(patchErr).NotTo(HaveOccurred())

Expand Down
3 changes: 1 addition & 2 deletions api/repositories/domain_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,7 @@ func (r *DomainRepo) UpdateDomain(ctx context.Context, authInfo authorization.In
}

err = r.klient.Patch(ctx, domain, func() error {
message.MetadataPatch.Apply(domain)
return nil
return message.MetadataPatch.Apply(domain)
})
if err != nil {
return DomainRecord{}, fmt.Errorf("failed to patch domain metadata: %w", apierrors.FromK8sError(err, DomainResourceType))
Expand Down
Loading