Skip to content

Commit

Permalink
Moved extra API URL validation to webhook
Browse files Browse the repository at this point in the history
  • Loading branch information
toszr committed Feb 2, 2022
1 parent 090f446 commit 0b75e7e
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 29 deletions.
4 changes: 0 additions & 4 deletions src/api/v1beta1/properties.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,10 +256,6 @@ func (dk *DynaKube) TenantUUID() (string, error) {
}

func tenantUUID(apiUrl string) (string, error) {
if !strings.HasSuffix(apiUrl, "/api") {
return "", fmt.Errorf("api url %s does not end with /api", apiUrl)
}

parsedUrl, err := url.Parse(apiUrl)
if err != nil {
return "", errors.WithMessagef(err, "problem parsing tenant id from url %s", apiUrl)
Expand Down
15 changes: 0 additions & 15 deletions src/api/v1beta1/properties_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,21 +143,6 @@ func TestTenantUUID(t *testing.T) {
)
})

t.Run("missing /api suffix in an API URL", func(t *testing.T) {
apiUrl := "https://google.com"
expectedTenantId := ""
expectedError := "api url https://google.com does not end with /api"

actualTenantId, err := tenantUUID(apiUrl)

assert.EqualErrorf(t, err, expectedError, "Expected that getting tenant id from '%s' will result in: '%v'",
apiUrl, expectedError,
)
assert.Equalf(t, expectedTenantId, actualTenantId, "Expected that tenant id of %s is %s, but found %s",
apiUrl, expectedTenantId, actualTenantId,
)
})

t.Run("suffix-only, relative API URL", func(t *testing.T) {
apiUrl := "/api"
expectedTenantId := ""
Expand Down
44 changes: 35 additions & 9 deletions src/webhook/validation/api_url.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package validation

import (
"net/url"
"strings"

dynatracev1beta1 "github.com/Dynatrace/dynatrace-operator/src/api/v1beta1"
Expand All @@ -11,26 +12,51 @@ const (
errorNoApiUrl = `The DynaKube's specification is missing the API URL or still has the example value set.
Make sure you correctly specify the URL in your custom resource.
`
noError = ""

errorInvalidApiUrl = `The DynaKube's specification has an invalid API URL value set.
Make sure you correctly specify the URL in your custom resource (including the /api postfix).
`
)

func noApiUrl(dv *dynakubeValidator, dynakube *dynatracev1beta1.DynaKube) string {
apiUrl := dynakube.Spec.APIURL

if !strings.HasSuffix(apiUrl, "/api") {
log.Info("api url does not end with /api", "apiUrl", apiUrl)
if apiUrl == exampleApiUrl {
log.Info("api url is an example url", "apiUrl", apiUrl)
return errorNoApiUrl
}

if apiUrl == exampleApiUrl {
log.Info("api url is an example url", "apiUrl", apiUrl)
if apiUrl == "" {
log.Info("requested dynakube has no api url", "name", dynakube.Name, "namespace", dynakube.Namespace)
return errorNoApiUrl
}

if apiUrl != "" {
return noError
return ""
}

func isInvalidApiUrl(dv *dynakubeValidator, dynakube *dynatracev1beta1.DynaKube) string {
apiUrl := dynakube.Spec.APIURL

if !strings.HasSuffix(apiUrl, "/api") {
log.Info("api url does not end with /api", "apiUrl", apiUrl)
return errorInvalidApiUrl
}

parsedUrl, err := url.Parse(apiUrl)
if err != nil {
log.Info("API URL is not a valid URL", "err", err.Error())
return errorInvalidApiUrl
}

fqdn := parsedUrl.Hostname()
hostnameWithDomains := strings.FieldsFunc(fqdn,
func(r rune) bool { return r == '.' },
)

if len(hostnameWithDomains) < 2 || len(hostnameWithDomains[0]) == 0 {
log.Info("problem getting tenant id from fqdn", "fqdn", fqdn)
return errorInvalidApiUrl
}

log.Info("requested dynakube has no api url", "name", dynakube.Name, "namespace", dynakube.Namespace)
return errorNoApiUrl
return ""
}
38 changes: 37 additions & 1 deletion src/webhook/validation/api_url_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package validation

import (
"strings"
"testing"

dynatracev1beta1 "github.com/Dynatrace/dynatrace-operator/src/api/v1beta1"
Expand All @@ -14,18 +15,53 @@ func TestHasApiUrl(t *testing.T) {
instance.Spec.APIURL = testApiUrl
assert.Empty(t, noApiUrl(nil, instance))

t.Run(`happy path`, func(t *testing.T) {
assertAllowedResponse(t, &dynatracev1beta1.DynaKube{
Spec: dynatracev1beta1.DynaKubeSpec{
APIURL: "https://tenantid.doma.in/api",
},
})
})
t.Run(`missing API URL`, func(t *testing.T) {
assertDeniedResponse(t, []string{errorNoApiUrl}, &dynatracev1beta1.DynaKube{
Spec: dynatracev1beta1.DynaKubeSpec{
APIURL: "",
},
})
})
t.Run(`invalid API URL`, func(t *testing.T) {
t.Run(`example API URL`, func(t *testing.T) {
assertDeniedResponse(t, []string{errorNoApiUrl}, &dynatracev1beta1.DynaKube{
Spec: dynatracev1beta1.DynaKubeSpec{
APIURL: exampleApiUrl,
},
})
})
t.Run(`invalid API URL (without /api suffix)`, func(t *testing.T) {
assertDeniedResponse(t, []string{errorInvalidApiUrl}, &dynatracev1beta1.DynaKube{
Spec: dynatracev1beta1.DynaKubeSpec{
APIURL: strings.TrimSuffix(exampleApiUrl, "/api"),
},
})
})
t.Run(`invalid API URL (not a Dynatrace environment)`, func(t *testing.T) {
assertDeniedResponse(t, []string{errorInvalidApiUrl}, &dynatracev1beta1.DynaKube{
Spec: dynatracev1beta1.DynaKubeSpec{
APIURL: "https://www.google.com",
},
})
})
t.Run(`invalid API URL (empty tenant ID)`, func(t *testing.T) {
assertDeniedResponse(t, []string{errorInvalidApiUrl}, &dynatracev1beta1.DynaKube{
Spec: dynatracev1beta1.DynaKubeSpec{
APIURL: "/api",
},
})
})
t.Run(`invalid API URL (missing domain)`, func(t *testing.T) {
assertDeniedResponse(t, []string{errorInvalidApiUrl}, &dynatracev1beta1.DynaKube{
Spec: dynatracev1beta1.DynaKubeSpec{
APIURL: "https://...tenantid/api",
},
})
})
}
1 change: 1 addition & 0 deletions src/webhook/validation/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ type validator func(dv *dynakubeValidator, dynakube *dynatracev1beta1.DynaKube)

var validators = []validator{
noApiUrl,
isInvalidApiUrl,
missingCSIDaemonSet,
conflictingActiveGateConfiguration,
invalidActiveGateCapabilities,
Expand Down

0 comments on commit 0b75e7e

Please sign in to comment.