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
5 changes: 3 additions & 2 deletions cmd/mapt/cmd/aws/services/snc.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
maptContext "github.com/redhat-developer/mapt/pkg/manager/context"
openshiftsnc "github.com/redhat-developer/mapt/pkg/provider/aws/action/snc"
sncApi "github.com/redhat-developer/mapt/pkg/target/service/snc"
"github.com/redhat-developer/mapt/pkg/target/service/snc/profile"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/spf13/viper"
Expand All @@ -24,7 +25,7 @@ const (
disableClusterReadinessDesc = "If this flag is set it will skip the checks for the cluster readiness. In this case the kubeconfig can not be generated"

sncProfile = "profile"
sncProfileDesc = "comma separated list of profiles to apply on the SNC cluster. Profiles available: virtualization"
sncProfileDesc = "comma separated list of profiles to apply on the SNC cluster. Profiles available: virtualization, serverless-serving, serverless-eventing, serverless"
)

func GetOpenshiftSNCCmd() *cobra.Command {
Expand Down Expand Up @@ -59,7 +60,7 @@ func createSNC() *cobra.Command {
}
profiles := viper.GetStringSlice(sncProfile)
computeReq := params.ComputeRequestArgs()
if sncApi.ProfilesRequireNestedVirt(profiles) {
if profile.RequireNestedVirt(profiles) {
computeReq.NestedVirt = true
}
if _, err := openshiftsnc.Create(
Expand Down
5 changes: 4 additions & 1 deletion docs/aws/openshift-snc.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,16 @@ mapt aws openshift-snc create \
--profile virtualization
```

Multiple profiles can be specified as a comma-separated list (e.g., `--profile virtualization,serverless`).
Multiple profiles can be specified as a comma-separated list (e.g., `--profile virtualization,serverless-serving`).

### Available profiles

| Profile | Description |
|---------|-------------|
| `virtualization` | Installs [OpenShift Virtualization](https://docs.openshift.com/container-platform/latest/virt/about_virt/about-virt.html) (CNV) on the cluster, enabling virtual machines to run on the single-node cluster. When this profile is selected, nested virtualization is automatically enabled on the cloud instance. Because standard Nitro-based instances do not expose `/dev/kvm`, a bare metal instance is required.|
| `serverless-serving` | Installs [OpenShift Serverless](https://docs.openshift.com/serverless/latest/about/about-serverless.html) and creates a KnativeServing instance, enabling serverless workloads (Knative Serving) on the cluster.|
| `serverless-eventing` | Installs [OpenShift Serverless](https://docs.openshift.com/serverless/latest/about/about-serverless.html) and creates a KnativeEventing instance, enabling event-driven workloads (Knative Eventing) on the cluster.|
| `serverless` | Installs [OpenShift Serverless](https://docs.openshift.com/serverless/latest/about/about-serverless.html) and creates both KnativeServing and KnativeEventing instances.|


### Adding new profiles
Expand Down
19 changes: 9 additions & 10 deletions pkg/provider/aws/action/snc/snc.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/redhat-developer/mapt/pkg/provider/util/command"
"github.com/redhat-developer/mapt/pkg/provider/util/security"
apiSNC "github.com/redhat-developer/mapt/pkg/target/service/snc"
"github.com/redhat-developer/mapt/pkg/target/service/snc/profile"
"github.com/redhat-developer/mapt/pkg/util"
"github.com/redhat-developer/mapt/pkg/util/logging"
resourcesUtil "github.com/redhat-developer/mapt/pkg/util/resources"
Expand Down Expand Up @@ -65,7 +66,7 @@ func Create(mCtxArgs *mc.ContextArgs, args *apiSNC.SNCArgs) (_ *apiSNC.SNCResult
return nil, err
}
// Validate profiles
if err := apiSNC.ValidateProfiles(args.Profiles); err != nil {
if err := profile.Validate(args.Profiles); err != nil {
return nil, err
}
// Compose request
Expand Down Expand Up @@ -266,18 +267,16 @@ func (r *openshiftSNCRequest) deploy(ctx *pulumi.Context) error {
pulumi.ToSecret(kubeconfig))
// Deploy profiles using Kubernetes provider
if len(r.profiles) > 0 {
k8sProvider, err := apiSNC.NewK8sProvider(ctx, "k8s-provider", kubeconfig)
k8sProvider, err := profile.NewK8sProvider(ctx, "k8s-provider", kubeconfig)
if err != nil {
return err
}
for _, profileName := range r.profiles {
if _, err := apiSNC.DeployProfile(ctx, profileName, &apiSNC.ProfileDeployArgs{
K8sProvider: k8sProvider,
Kubeconfig: kubeconfig,
Prefix: *r.prefix,
}); err != nil {
return err
}
if err := profile.Deploy(ctx, r.profiles, &profile.DeployArgs{
K8sProvider: k8sProvider,
Kubeconfig: kubeconfig,
Prefix: *r.prefix,
}); err != nil {
logging.Warnf("profile deployment failed: %v", err)
}
}
return nil
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package snc
package profile

import (
"context"
"fmt"
"strings"
"time"

"github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
logging "github.com/redhat-developer/mapt/pkg/util/logging"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
Expand All @@ -16,13 +14,6 @@ import (
"k8s.io/client-go/tools/clientcmd"
)

// NewK8sProvider creates a Pulumi Kubernetes provider from a kubeconfig string output.
func NewK8sProvider(ctx *pulumi.Context, name string, kubeconfig pulumi.StringOutput) (*kubernetes.Provider, error) {
return kubernetes.NewProvider(ctx, name, &kubernetes.ProviderArgs{
Kubeconfig: kubeconfig,
})
}

// waitForCRCondition polls a custom resource until a nested field matches the expected value.
// jsonPath is a dot-separated path into the object (e.g. "status.phase" or
// "status.conditions[?(@.type==\"Available\")].status") but here we use explicit
Expand Down
96 changes: 96 additions & 0 deletions pkg/target/service/snc/profile/profile.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package profile

import (
"fmt"
"slices"

"github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

const (
ProfileVirtualization = "virtualization"
ProfileServerlessServing = "serverless-serving"
ProfileServerlessEventing = "serverless-eventing"
ProfileServerless = "serverless"
)

// validProfiles is the single source of truth for supported profile names.
var validProfiles = []string{ProfileVirtualization, ProfileServerlessServing, ProfileServerlessEventing, ProfileServerless}

// DeployArgs holds the arguments needed by a profile to deploy
// its resources on the SNC cluster.
type DeployArgs struct {
K8sProvider *kubernetes.Provider
Kubeconfig pulumi.StringOutput
Prefix string
Deps []pulumi.Resource
}

// Validate checks that all requested profiles are supported.
func Validate(profiles []string) error {
for _, p := range profiles {
if !slices.Contains(validProfiles, p) {
return fmt.Errorf("profile %q is not supported for SNC. Supported profiles: %v", p, validProfiles)
}
}
return nil
}

// Deploy deploys all requested profiles on the SNC cluster.
// It ensures shared dependencies (e.g. the Serverless operator) are only
// installed once, even when multiple serverless profiles are requested.
func Deploy(ctx *pulumi.Context, profiles []string, args *DeployArgs) error {
needServing := false
needEventing := false

for _, p := range profiles {
switch p {
case ProfileVirtualization:
if _, err := deployVirtualization(ctx, args); err != nil {
return err
}
case ProfileServerlessServing:
needServing = true
case ProfileServerlessEventing:
needEventing = true
case ProfileServerless:
needServing = true
needEventing = true
default:
return fmt.Errorf("profile %q has no deploy function", p)
}
}

if needServing || needEventing {
operatorReady, err := deployServerlessOperator(ctx, args)
if err != nil {
return err
}
if needServing {
if _, err := deployKnativeServing(ctx, args, operatorReady); err != nil {
return err
}
}
if needEventing {
if _, err := deployKnativeEventing(ctx, args, operatorReady); err != nil {
return err
}
}
}

return nil
}

// RequireNestedVirt returns true if any of the given profiles
// requires nested virtualization on the compute instance.
func RequireNestedVirt(profiles []string) bool {
return slices.Contains(profiles, ProfileVirtualization)
}

// NewK8sProvider creates a Pulumi Kubernetes provider from a kubeconfig string output.
func NewK8sProvider(ctx *pulumi.Context, name string, kubeconfig pulumi.StringOutput) (*kubernetes.Provider, error) {
return kubernetes.NewProvider(ctx, name, &kubernetes.ProviderArgs{
Kubeconfig: kubeconfig,
})
}
Loading