Skip to content

Commit

Permalink
Add support for apiPaths (#869)
Browse files Browse the repository at this point in the history
  • Loading branch information
bsquizz authored Oct 11, 2023
1 parent d423e84 commit 4f58b5b
Show file tree
Hide file tree
Showing 15 changed files with 238 additions and 35 deletions.
9 changes: 8 additions & 1 deletion apis/cloud.redhat.com/v1alpha1/clowdapp_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ type Job struct {
// WebDeprecated defines a boolean flag to help distinguish from the newer WebServices
type WebDeprecated bool

// A string representing an API path that should route to this app for Clowder-managed Ingresses (in format "/api/somepath/")
// +kubebuilder:validation:Pattern=`^\/api\/[a-zA-Z0-9-]+\/$`
type APIPath string

// PublicWebService is the definition of the public web service. There can be only
// one public service managed by Clowder.
type PublicWebService struct {
Expand All @@ -145,9 +149,12 @@ type PublicWebService struct {
// configuration in the cdappconfig.
Enabled bool `json:"enabled,omitempty"`

// APIPath describes the api path that will be configured to serve this backend from.
// (DEPRECATED, use apiPaths instead) Configures a path named '/api/<apiPath>/' that this app will serve requests from.
APIPath string `json:"apiPath,omitempty"`

// Defines a list of API paths (each matching format: "/api/some-path/") that this app will serve requests from.
APIPaths []APIPath `json:"apiPaths,omitempty"`

// WhitelistPaths define the paths that do not require authentication
WhitelistPaths []string `json:"whitelistPaths,omitempty"`
}
Expand Down
5 changes: 5 additions & 0 deletions apis/cloud.redhat.com/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 14 additions & 2 deletions config/crd/bases/cloud.redhat.com_clowdapps.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2950,9 +2950,21 @@ spec:
by Clowder.
properties:
apiPath:
description: APIPath describes the api path that will
be configured to serve this backend from.
description: (DEPRECATED, use apiPaths instead) Configures
a path named '/api/<apiPath>/' that this app will
serve requests from.
type: string
apiPaths:
description: 'Defines a list of API paths (each matching
format: "/api/some-path/") that this app will serve
requests from.'
items:
description: A string representing an API path that
should route to this app for Clowder-managed Ingresses
(in format "/api/somepath/")
pattern: ^\/api\/[a-zA-Z0-9-]+\/$
type: string
type: array
enabled:
description: Enabled describes if Clowder should enable
the public service and provide the configuration in
Expand Down
7 changes: 5 additions & 2 deletions controllers/cloud.redhat.com/config/types.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 6 additions & 7 deletions controllers/cloud.redhat.com/providers/dependencies/impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
crd "github.com/RedHatInsights/clowder/apis/cloud.redhat.com/v1alpha1"
"github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/config"
"github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/errors"
provutils "github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/providers/utils"
"github.com/RedHatInsights/rhc-osdk-utils/utils"
)

Expand Down Expand Up @@ -119,15 +120,11 @@ func processAppEndpoints(
}

// If app has public endpoint, add it to app config

for _, deployment := range depApp.Spec.Deployments {
// avoid implicit memory aliasing
innerDeployment := deployment

apiPath := deployment.WebServices.Public.APIPath

if apiPath == "" {
apiPath = depApp.GetDeploymentNamespacedName(&innerDeployment).Name
}
apiPaths := provutils.GetAPIPaths(&innerDeployment, depApp.GetDeploymentNamespacedName(&innerDeployment).Name)

if bool(innerDeployment.Web) || innerDeployment.WebServices.Public.Enabled {
name := depApp.GetDeploymentNamespacedName(&innerDeployment).Name
Expand All @@ -137,7 +134,9 @@ func processAppEndpoints(
Name: innerDeployment.Name,
App: depApp.Name,
TlsPort: utils.IntPtr(int(tlsPort)),
ApiPath: fmt.Sprintf("/api/%s/", apiPath),
// if app has multiple paths set, set apiPath to first name for backward compatibility
ApiPath: apiPaths[0],
ApiPaths: apiPaths,
})
}
if innerDeployment.WebServices.Private.Enabled {
Expand Down
19 changes: 19 additions & 0 deletions controllers/cloud.redhat.com/providers/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -288,3 +288,22 @@ func AddCertVolume(d *core.PodSpec, dnn string) {
d.InitContainers[i].VolumeMounts = vms
}
}

func GetAPIPaths(deployment *crd.Deployment, defaultPath string) []string {
apiPaths := []string{}
if deployment.WebServices.Public.APIPaths == nil {
// singular apiPath is deprecated, use it only if apiPaths is undefined
apiPath := deployment.WebServices.Public.APIPath
if apiPath == "" {
apiPath = defaultPath
}
apiPaths = []string{fmt.Sprintf("/api/%s/", apiPath)}
} else {
// apiPaths was defined, use it and ignore 'apiPath'
for _, path := range deployment.WebServices.Public.APIPaths {
// convert crd.APIPath array items into plain strings
apiPaths = append(apiPaths, string(path))
}
}
return apiPaths
}
36 changes: 19 additions & 17 deletions controllers/cloud.redhat.com/providers/web/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,11 +190,7 @@ func (web *localWebProvider) createIngress(app *crd.ClowdApp, deployment *crd.De
ingressClass = "nginx"
}

apiPath := deployment.WebServices.Public.APIPath

if apiPath == "" {
apiPath = nn.Name
}
apiPaths := provutils.GetAPIPaths(deployment, nn.Name)

netobj.Spec = networking.IngressSpec{
TLS: []networking.IngressTLS{{
Expand All @@ -206,23 +202,29 @@ func (web *localWebProvider) createIngress(app *crd.ClowdApp, deployment *crd.De
Host: web.Env.Status.Hostname,
IngressRuleValue: networking.IngressRuleValue{
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{{
Path: fmt.Sprintf("/api/%s/", apiPath),
PathType: (*networking.PathType)(utils.StringPtr("Prefix")),
Backend: networking.IngressBackend{
Service: &networking.IngressServiceBackend{
Name: nn.Name,
Port: networking.ServiceBackendPort{
Name: "auth",
},
},
},
}},
Paths: []networking.HTTPIngressPath{},
},
},
},
},
}

for _, path := range apiPaths {
path := networking.HTTPIngressPath{
Path: path,
PathType: (*networking.PathType)(utils.StringPtr("Prefix")),
Backend: networking.IngressBackend{
Service: &networking.IngressServiceBackend{
Name: nn.Name,
Port: networking.ServiceBackendPort{
Name: "auth",
},
},
},
}
netobj.Spec.Rules[0].IngressRuleValue.HTTP.Paths = append(netobj.Spec.Rules[0].IngressRuleValue.HTTP.Paths, path)
}

return web.Cache.Update(WebIngress, netobj)
}

Expand Down
16 changes: 14 additions & 2 deletions deploy-mutate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3025,9 +3025,21 @@ objects:
managed by Clowder.
properties:
apiPath:
description: APIPath describes the api path that will
be configured to serve this backend from.
description: (DEPRECATED, use apiPaths instead) Configures
a path named '/api/<apiPath>/' that this app will
serve requests from.
type: string
apiPaths:
description: 'Defines a list of API paths (each matching
format: "/api/some-path/") that this app will serve
requests from.'
items:
description: A string representing an API path that
should route to this app for Clowder-managed Ingresses
(in format "/api/somepath/")
pattern: ^\/api\/[a-zA-Z0-9-]+\/$
type: string
type: array
enabled:
description: Enabled describes if Clowder should enable
the public service and provide the configuration
Expand Down
16 changes: 14 additions & 2 deletions deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3025,9 +3025,21 @@ objects:
managed by Clowder.
properties:
apiPath:
description: APIPath describes the api path that will
be configured to serve this backend from.
description: (DEPRECATED, use apiPaths instead) Configures
a path named '/api/<apiPath>/' that this app will
serve requests from.
type: string
apiPaths:
description: 'Defines a list of API paths (each matching
format: "/api/some-path/") that this app will serve
requests from.'
items:
description: A string representing an API path that
should route to this app for Clowder-managed Ingresses
(in format "/api/somepath/")
pattern: ^\/api\/[a-zA-Z0-9-]+\/$
type: string
type: array
enabled:
description: Enabled describes if Clowder should enable
the public service and provide the configuration
Expand Down
17 changes: 16 additions & 1 deletion docs/antora/modules/ROOT/pages/api_reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,20 @@ Package v1alpha1 contains API Schema definitions for the cloud.redhat.com v1alph



[id="{anchor_prefix}-github-com-redhatinsights-clowder-apis-cloud-redhat-com-v1alpha1-apipath"]
==== APIPath

_Underlying type:_ _string_

A string representing an API path that should route to this app for Clowder-managed Ingresses (in format "/api/somepath/")

.Appears In:
****
- xref:{anchor_prefix}-github-com-redhatinsights-clowder-apis-cloud-redhat-com-v1alpha1-publicwebservice[$$PublicWebService$$]
****



[id="{anchor_prefix}-github-com-redhatinsights-clowder-apis-cloud-redhat-com-v1alpha1-appinfo"]
==== AppInfo

Expand Down Expand Up @@ -1358,7 +1372,8 @@ PublicWebService is the definition of the public web service. There can be only
|===
| Field | Description
| *`enabled`* __boolean__ | Enabled describes if Clowder should enable the public service and provide the configuration in the cdappconfig.
| *`apiPath`* __string__ | APIPath describes the api path that will be configured to serve this backend from.
| *`apiPath`* __string__ | (DEPRECATED, use apiPaths instead) Configures a path named '/api/<apiPath>/' that this app will serve requests from.
| *`apiPaths`* __xref:{anchor_prefix}-github-com-redhatinsights-clowder-apis-cloud-redhat-com-v1alpha1-apipath[$$APIPath$$] array__ | Defines a list of API paths (each matching format: "/api/some-path/") that this app will serve requests from.
| *`whitelistPaths`* __string array__ | WhitelistPaths define the paths that do not require authentication
|===

Expand Down
2 changes: 1 addition & 1 deletion tests/kuttl/test-basic-app/02-json-asserts.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ commands:
- script: jq -r '.endpoints[] | select(.app == "puptoo") | select(.name == "processor") | .port == 8000' -e < /tmp/test-basic-app-json
- script: jq -r '.endpoints[] | select(.app == "puptoo") | select(.name == "processor2") | .port == 8000' -e < /tmp/test-basic-app-json
- script: jq -r '.endpoints[] | select(.app == "puptoo") | select(.name == "processor") | .apiPath == "/api/puptoo-processor/"' -e < /tmp/test-basic-app-json

- script: jq -r '.endpoints[] | select(.app == "puptoo") | select(.name == "processor") | .apiPaths[0] == "/api/puptoo-processor/"' -e < /tmp/test-basic-app-json

- script: jq -r '.privateEndpoints[] | select(.app == "puptoo") | select(.name == "processor") | .hostname == "puptoo-processor.test-basic-app.svc"' -e < /tmp/test-basic-app-json
- script: jq -r '.privateEndpoints[] | select(.app == "puptoo") | select(.name == "processor2") | .hostname == "puptoo-processor2.test-basic-app.svc"' -e < /tmp/test-basic-app-json
Expand Down
38 changes: 38 additions & 0 deletions tests/kuttl/test-ephemeral-gateway/01-pods.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,44 @@ spec:
enabled: true
apiPath: puptoo
---
apiVersion: cloud.redhat.com/v1alpha1
kind: ClowdApp
metadata:
name: puptoo-2paths
namespace: test-ephemeral-gateway
spec:
envName: test-ephemeral-gateway
deployments:
- name: processor
podSpec:
image: quay.io/psav/clowder-hello:01
readinessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 8000
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
livenessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 8000
scheme: HTTP
initialDelaySeconds: 5
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
webServices:
public:
enabled: true
apiPaths:
- /api/puptoo1/
- /api/puptoo2/
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
Expand Down
1 change: 1 addition & 0 deletions tests/kuttl/test-ephemeral-gateway/02-json-assert.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ apiVersion: kuttl.dev/v1beta1
kind: TestStep
commands:
- script: sleep 2
- script: rm -fr /tmp/test-ephemeral-gateway
- script: mkdir -p /tmp/test-ephemeral-gateway/
- script: kubectl get secret -n test-ephemeral-gateway -o json test-ephemeral-gateway-test-cert | jq -r '.data["ca.crt"] | @base64d' > /tmp/test-ephemeral-gateway/ca.pem
- script: kubectl get secret -n test-ephemeral-gateway -o json test-ephemeral-gateway-test-cert | jq -r '.data["tls.crt"] | @base64d' > /tmp/test-ephemeral-gateway/tls.crt
Expand Down
Loading

0 comments on commit 4f58b5b

Please sign in to comment.