Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CORS-3835: Add gcp endpoints to the installer config #9363

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
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
38 changes: 38 additions & 0 deletions pkg/types/gcp/platform.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,23 @@ import (
"github.com/openshift/installer/pkg/types/dns"
)

const (
// CloudResourceManagerServiceName is the name and internal key for the cloud resource manager API endpoint
CloudResourceManagerServiceName = "cloudresourcemanager"
// ComputeServiceName is the name and internal key for the compute API endpoint
ComputeServiceName = "compute"
// DNSServiceName is the name and internal key for the DNS API endpoint
DNSServiceName = "dns"
// FileServiceName is the name and internal key for the file API endpoint
FileServiceName = "file"
// IAMServiceName is the name and internal key for the IAM API endpoint
IAMServiceName = "iam"
// ServiceUsageServiceName is the name and internal key for the service usage API endpoint
ServiceUsageServiceName = "serviceusage"
// StorageServiceName is the name and internal key for the storage API endpoint
StorageServiceName = "storage"
)

// Platform stores all the global configuration that all machinesets
// use.
type Platform struct {
Expand Down Expand Up @@ -58,6 +75,27 @@ type Platform struct {
// +default="Disabled"
// +kubebuilder:validation:Enum="Enabled";"Disabled"
UserProvisionedDNS dns.UserProvisionedDNS `json:"userProvisionedDNS,omitempty"`

// ServiceEndpoints list contains custom endpoints which will override default
// service endpoint of GCP Services.
// There must be only one ServiceEndpoint for a service.
// +optional
ServiceEndpoints []ServiceEndpoint `json:"serviceEndpoints,omitempty"`
}

// ServiceEndpoint store the configuration for services to
// override existing defaults of GCP Services.
type ServiceEndpoint struct {
// Name is the name of the GCP service.
// This must be provided and cannot be empty.
Name string `json:"name"`

// URL is fully qualified URI with scheme https, that overrides the default generated
// endpoint for a client.
// This must be provided and cannot be empty.
//
// +kubebuilder:validation:Pattern=`^https://`
URL string `json:"url"`
}

// UserLabel is a label to apply to GCP resources created for the cluster.
Expand Down
59 changes: 59 additions & 0 deletions pkg/types/gcp/validation/platform.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package validation

import (
"fmt"
"net/url"
"regexp"
"sort"

"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/validation/field"

"github.com/openshift/installer/pkg/types"
Expand Down Expand Up @@ -76,6 +78,16 @@ var (

// userLabelKeyPrefixRegex is for verifying that the label key does not contain restricted prefixes.
userLabelKeyPrefixRegex = regexp.MustCompile(`^(?i)(kubernetes\-io|openshift\-io)`)

supportedEndpointNames = sets.New(
gcp.CloudResourceManagerServiceName,
gcp.ComputeServiceName,
gcp.DNSServiceName,
gcp.FileServiceName,
gcp.IAMServiceName,
gcp.ServiceUsageServiceName,
gcp.StorageServiceName,
)
)

const (
Expand Down Expand Up @@ -118,6 +130,7 @@ func ValidatePlatform(p *gcp.Platform, fldPath *field.Path, ic *types.InstallCon

// check if configured userLabels are valid.
allErrs = append(allErrs, validateUserLabels(p.UserLabels, fldPath.Child("userLabels"))...)
allErrs = append(allErrs, validateServiceEndpoints(p.ServiceEndpoints, fldPath.Child("serviceEndpoints"))...)

return allErrs
}
Expand Down Expand Up @@ -161,3 +174,49 @@ func validateLabel(key, value string) error {
}
return nil
}

func validateServiceEndpoints(endpoints []gcp.ServiceEndpoint, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
tracker := map[string]int{}
for idx, e := range endpoints {
fldp := fldPath.Index(idx)
if !supportedEndpointNames.Has(e.Name) {
allErrs = append(allErrs, field.NotSupported(fldp.Child("name"), e.Name, sets.List(supportedEndpointNames)))
}
if _, ok := tracker[e.Name]; ok {
allErrs = append(allErrs, field.Duplicate(fldp.Child("name"), e.Name))
} else {
tracker[e.Name] = idx
}

if err := validateServiceURL(e.URL); err != nil {
allErrs = append(allErrs, field.Invalid(fldp.Child("url"), e.URL, err.Error()))
}
}
return allErrs
}

var schemeRE = regexp.MustCompile("^([^:]+)://")

func validateServiceURL(uri string) error {
endpoint := uri
if !schemeRE.MatchString(endpoint) {
scheme := "https"
endpoint = fmt.Sprintf("%s://%s", scheme, endpoint)
}

u, err := url.Parse(endpoint)
if err != nil {
return err
}
if u.Hostname() == "" {
return fmt.Errorf("host cannot be empty, empty host provided")
}
if s := u.Scheme; s != "https" {
return fmt.Errorf("invalid scheme %s, only https allowed", s)
}
// Unlike AWS, the format can include a path without request parameters see
// https://cloud.google.com/storage/docs/request-endpoints as an example.

return nil
}
82 changes: 82 additions & 0 deletions pkg/types/gcp/validation/platform_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,88 @@ func TestValidatePlatform(t *testing.T) {
credentialsMode: types.MintCredentialsMode,
valid: false,
},
{
name: "invalid gcp endpoint blank name",
platform: &gcp.Platform{
Region: "us-east1",
ServiceEndpoints: []gcp.ServiceEndpoint{
{
Name: "",
URL: "https://my-custom-endpoint.example.com/copmute/v1/",
},
},
},
valid: false,
},
{
name: "invalid gcp endpoint invalid name",
platform: &gcp.Platform{
Region: "us-east1",
ServiceEndpoints: []gcp.ServiceEndpoint{
{
Name: "badname",
URL: "https://my-custom-endpoint.example.com/copmute/v1/",
},
},
},
valid: false,
},
{
name: "invalid gcp endpoint duplicate name",
platform: &gcp.Platform{
Region: "us-east1",
ServiceEndpoints: []gcp.ServiceEndpoint{
{
Name: "compute",
URL: "https://my-custom-endpoint.example.com/compute/v1/",
},
{
Name: "compute",
URL: "https://my-custom-endpoint.example.com/compute/v2/",
},
},
},
valid: false,
},
{
name: "invalid gcp endpoint url blank",
platform: &gcp.Platform{
Region: "us-east1",
ServiceEndpoints: []gcp.ServiceEndpoint{
{
Name: "compute",
URL: "",
},
},
},
valid: false,
},
{
name: "invalid scheme gcp endpoint url",
platform: &gcp.Platform{
Region: "us-east1",
ServiceEndpoints: []gcp.ServiceEndpoint{
{
Name: "compute",
URL: "http://my-custom-endpoint.example.com/compute/v1/",
},
},
},
valid: false,
},
{
name: "valid gcp endpoint",
platform: &gcp.Platform{
Region: "us-east1",
ServiceEndpoints: []gcp.ServiceEndpoint{
{
Name: "compute",
URL: "https://my-custom-endpoint.example.com/compute/v1/",
},
},
},
valid: true,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
Expand Down