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

[WIP] Support multi compute platform #7532

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
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
425 changes: 425 additions & 0 deletions pkg/corerp/backend/containers/createorupdate.go

Large diffs are not rendered by default.

60 changes: 60 additions & 0 deletions pkg/corerp/backend/containers/delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
Copyright 2023 The Radius Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package containers

import (
"context"

v1 "github.com/radius-project/radius/pkg/armrpc/api/v1"
ctrl "github.com/radius-project/radius/pkg/armrpc/asyncoperation/controller"
"github.com/radius-project/radius/pkg/ucp/resources"
)

var _ ctrl.Controller = (*DeleteResource)(nil)

// DeleteResource is the async operation controller to delete Applications.Core/Containers resource.
type DeleteResource struct {
ctrl.BaseController
}

// NewDeleteResource creates a new DeleteResource controller with the given options.
func NewDeleteResource(opts ctrl.Options) (ctrl.Controller, error) {
return &DeleteResource{ctrl.NewBaseAsyncController(opts)}, nil
}

// Run retrieves a resource from storage, parses its ID, gets its data model, converts it to a deployment
// data model, deletes the resource from the deployment processor, and deletes the resource from storage.
// It returns an error if any of these steps fail.
func (c *DeleteResource) Run(ctx context.Context, request *ctrl.Request) (ctrl.Result, error) {
_, err := c.StorageClient().Get(ctx, request.ResourceID)
if err != nil {
return ctrl.NewFailedResult(v1.ErrorDetails{Message: err.Error()}), err
}

// This code is general and we might be processing an async job for a resource or a scope, so using the general Parse function.
_, err = resources.Parse(request.ResourceID)
if err != nil {
return ctrl.Result{}, err
}

err = c.StorageClient().Delete(ctx, request.ResourceID)
if err != nil {
return ctrl.Result{}, err
}

return ctrl.Result{}, err
}
122 changes: 122 additions & 0 deletions pkg/corerp/backend/containers/util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package containers

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

v1 "github.com/radius-project/radius/pkg/armrpc/api/v1"
"github.com/radius-project/radius/pkg/corerp/datamodel"
"github.com/radius-project/radius/pkg/ucp/resources"
resources_radius "github.com/radius-project/radius/pkg/ucp/resources/radius"
corev1 "k8s.io/api/core/v1"
)

func GetDependencyIDs(ctx context.Context, resource *datamodel.ContainerResource) (radiusResourceIDs []resources.ID, azureResourceIDs []resources.ID, err error) {
properties := resource.Properties

// Right now we only have things in connections and ports as rendering dependencies - we'll add more things
// in the future... eg: volumes
//
// Anywhere we accept a resource ID in the model should have its value returned from here

// ensure that users cannot use DNS-SD and httproutes simultaneously.
for _, connection := range properties.Connections {
if isURL(connection.Source) {
continue
}

// if the source is not a URL, it either a resourceID or invalid.
resourceID, err := resources.ParseResource(connection.Source)
if err != nil {
return nil, nil, v1.NewClientErrInvalidRequest(fmt.Sprintf("invalid source: %s. Must be either a URL or a valid resourceID", connection.Source))
}

// Non-radius Azure connections that are accessible from Radius container resource.
if connection.IAM.Kind.IsKind(datamodel.KindAzure) {
azureResourceIDs = append(azureResourceIDs, resourceID)
continue
}

if resources_radius.IsRadiusResource(resourceID) {
radiusResourceIDs = append(radiusResourceIDs, resourceID)
continue
}
}

for _, port := range properties.Container.Ports {
provides := port.Provides

// if provides is empty, skip this port. A service for this port will be generated later on.
if provides == "" {
continue
}

resourceID, err := resources.ParseResource(provides)
if err != nil {
return nil, nil, v1.NewClientErrInvalidRequest(err.Error())
}

if resources_radius.IsRadiusResource(resourceID) {
radiusResourceIDs = append(radiusResourceIDs, resourceID)
continue
}
}

for _, volume := range properties.Container.Volumes {
switch volume.Kind {
case datamodel.Persistent:
resourceID, err := resources.ParseResource(volume.Persistent.Source)
if err != nil {
return nil, nil, v1.NewClientErrInvalidRequest(err.Error())
}

if resources_radius.IsRadiusResource(resourceID) {
radiusResourceIDs = append(radiusResourceIDs, resourceID)
continue
}
}
}

return radiusResourceIDs, azureResourceIDs, nil
}

func getSortedKeys(env map[string]corev1.EnvVar) []string {
keys := []string{}
for k := range env {
key := k
keys = append(keys, key)
}

sort.Strings(keys)
return keys
}

func isURL(input string) bool {
_, err := url.ParseRequestURI(input)

// if first character is a slash, it's not a URL. It's a path.
if input == "" || err != nil || input[0] == '/' {
return false
}
return true
}

func parseURL(sourceURL string) (scheme, hostname, port string, err error) {
u, err := url.Parse(sourceURL)
if err != nil {
return "", "", "", err
}

scheme = u.Scheme
host := u.Host

hostname, port, err = net.SplitHostPort(host)
if err != nil {
return "", "", "", err
}

return scheme, hostname, port, nil
}
11 changes: 11 additions & 0 deletions pkg/corerp/frontend/controller/containers/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"context"
"encoding/json"
"fmt"
"net/url"

appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
Expand All @@ -38,6 +39,16 @@ const (
podTargetProperty = "$.properties.runtimes.kubernetes.pod"
)

func isURL(input string) bool {
_, err := url.ParseRequestURI(input)

// if first character is a slash, it's not a URL. It's a path.
if input == "" || err != nil || input[0] == '/' {
return false
}
return true
}

// ValidateAndMutateRequest checks if the newResource has a user-defined identity and if so, returns a bad request
// response, otherwise it sets the identity of the newResource to the identity of the oldResource if it exists.
func ValidateAndMutateRequest(ctx context.Context, newResource, oldResource *datamodel.ContainerResource, options *controller.Options) (rest.Response, error) {
Expand Down
64 changes: 64 additions & 0 deletions pkg/platform-provider/factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
Copyright 2023 The Radius Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package platformprovider

import "sync"

// Factory is a function that returns a new platform provider.
type Factory func() (Provider, error)

var (
registryMu sync.Mutex
providerRegistry = make(map[string]Factory)
)

// Register registers a platformprovider.Factory by name.
func Register(name string, platform Factory) {
registryMu.Lock()
defer registryMu.Unlock()

if platform == nil {
panic("platform-provider: Register platform is nil")
}

if _, dup := providerRegistry[name]; dup {
panic("platform-provider: Register called twice for platform " + name)
}
providerRegistry[name] = platform
}

// GetPlatform creates an instance of the named platform provider, or nil if the name is not registered.
func GetPlatform(name string) (Provider, error) {
registryMu.Lock()
defer registryMu.Unlock()

platform, ok := providerRegistry[name]
if !ok {
return nil, nil
}

return platform()
}

// NewProvider creates an instance of the named platform provider, or nil if the name is not registered.
func NewProvider(name string) (Provider, error) {
if name == "" {
return nil, nil
}

return GetPlatform(name)
}
Loading
Loading