Skip to content

Commit

Permalink
Add recipe support to dynamic RP
Browse files Browse the repository at this point in the history
This change implements recipe support fo the dynamic RP. With this feature, a user-defined-type can declare the SupportsRecipes capability, and then it will be processed by the recipe engine.

The main change here is to the core datamodel interfaces used by our shared controllers. These interfaces have some problematic definitions that rely on returning pointers to go-structs so they can be mutated and saved to the database. This general approach does not work for UDT, or any other design besides the portable resources.

To understand this problem in more detail, consider that ".properties.status" is user-defined for a UDT. The existing interfaces assume that all resources have the same go struct at ".properties.status". Because UDT is extensible, we cannot write a single go-struct that captures all possible status values, but that's required by the current interfaces. Clearly we need something more flexible.

I took the approach of making minimal changes in this PR, but we should improve the design longer-term.

Signed-off-by: Ryan Nowak <[email protected]>
  • Loading branch information
rynowak authored and lakshmimsft committed Jan 22, 2025
1 parent a665232 commit 84c52fd
Show file tree
Hide file tree
Showing 42 changed files with 1,193 additions and 372 deletions.
16 changes: 8 additions & 8 deletions pkg/armrpc/rest/results.go
Original file line number Diff line number Diff line change
Expand Up @@ -375,27 +375,27 @@ type BadRequestResponse struct {
}

// NewLinkedResourceUpdateErrorResponse represents a HTTP 400 with an error message when user updates environment id and application id.
func NewLinkedResourceUpdateErrorResponse(resourceID resources.ID, oldProp *rpv1.BasicResourceProperties, newProp *rpv1.BasicResourceProperties) Response {
func NewLinkedResourceUpdateErrorResponse(resourceID resources.ID, oldScope rpv1.BasicResourcePropertiesAdapter, newScope rpv1.BasicResourcePropertiesAdapter) Response {
newAppEnv := ""
if newProp.Application != "" {
name := newProp.Application
if rid, err := resources.ParseResource(newProp.Application); err == nil {
if newScope.ApplicationID() != "" {
name := newScope.ApplicationID()
if rid, err := resources.ParseResource(newScope.ApplicationID()); err == nil {
name = rid.Name()
}
newAppEnv += fmt.Sprintf("'%s' application", name)
}
if newProp.Environment != "" {
if newScope.EnvironmentID() != "" {
if newAppEnv != "" {
newAppEnv += " and "
}
name := newProp.Environment
if rid, err := resources.ParseResource(newProp.Environment); err == nil {
name := newScope.EnvironmentID()
if rid, err := resources.ParseResource(newScope.EnvironmentID()); err == nil {
name = rid.Name()
}
newAppEnv += fmt.Sprintf("'%s' environment", name)
}

message := fmt.Sprintf(LinkedResourceUpdateErrorFormat, resourceID.Name(), resourceID.Name(), newAppEnv, oldProp.Application, oldProp.Environment, resourceID.Name())
message := fmt.Sprintf(LinkedResourceUpdateErrorFormat, resourceID.Name(), resourceID.Name(), newAppEnv, oldScope.ApplicationID(), oldScope.EnvironmentID(), resourceID.Name())
return &BadRequestResponse{
Body: v1.ErrorResponse{
Error: v1.ErrorDetails{
Expand Down
2 changes: 1 addition & 1 deletion pkg/corerp/backend/deployment/deploymentprocessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ func (dp *deploymentProcessor) Render(ctx context.Context, resourceID resources.

c := app.Properties.Status.Compute
// Override environment-scope namespace with application-scope kubernetes namespace.
if c != nil && c.Kind == rpv1.KubernetesComputeKind {
if c.Kind == rpv1.KubernetesComputeKind {
envOptions.Namespace = c.KubernetesCompute.Namespace
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/corerp/datamodel/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func (c *Application) OutputResources() []rpv1.OutputResource {
}

// ResourceMetadata returns the BasicResourceProperties of the Application instance.
func (h *Application) ResourceMetadata() *rpv1.BasicResourceProperties {
func (h *Application) ResourceMetadata() rpv1.BasicResourcePropertiesAdapter {
return &h.Properties.BasicResourceProperties
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/corerp/datamodel/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func (c *ContainerResource) OutputResources() []rpv1.OutputResource {
}

// ResourceMetadata returns the BasicResourceProperties of the ContainerResource instance.
func (h *ContainerResource) ResourceMetadata() *rpv1.BasicResourceProperties {
func (h *ContainerResource) ResourceMetadata() rpv1.BasicResourcePropertiesAdapter {
return &h.Properties.BasicResourceProperties
}

Expand Down
9 changes: 7 additions & 2 deletions pkg/corerp/datamodel/extender.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func (r *Extender) OutputResources() []rpv1.OutputResource {
}

// ResourceMetadata returns the BasicResourceProperties of the Extender resource.
func (r *Extender) ResourceMetadata() *rpv1.BasicResourceProperties {
func (r *Extender) ResourceMetadata() rpv1.BasicResourcePropertiesAdapter {
return &r.Properties.BasicResourceProperties
}

Expand All @@ -59,13 +59,18 @@ func (r *Extender) ResourceTypeName() string {

// Recipe returns the ResourceRecipe associated with the Extender if the ResourceProvisioning is not set to Manual,
// otherwise it returns nil.
func (r *Extender) Recipe() *portableresources.ResourceRecipe {
func (r *Extender) GetRecipe() *portableresources.ResourceRecipe {
if r.Properties.ResourceProvisioning == portableresources.ResourceProvisioningManual {
return nil
}
return &r.Properties.ResourceRecipe
}

// SetRecipe sets the recipe information.
func (r *Extender) SetRecipe(recipe *portableresources.ResourceRecipe) {
r.Properties.ResourceRecipe = *recipe
}

// ExtenderProperties represents the properties of Extender resource.
type ExtenderProperties struct {
rpv1.BasicResourceProperties
Expand Down
2 changes: 1 addition & 1 deletion pkg/corerp/datamodel/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func (g *Gateway) OutputResources() []rpv1.OutputResource {
}

// ResourceMetadata returns the BasicResourceProperties of the Gateway instance.
func (h *Gateway) ResourceMetadata() *rpv1.BasicResourceProperties {
func (h *Gateway) ResourceMetadata() rpv1.BasicResourcePropertiesAdapter {
return &h.Properties.BasicResourceProperties
}

Expand Down
4 changes: 2 additions & 2 deletions pkg/corerp/datamodel/secretstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ func (s *SecretStore) OutputResources() []rpv1.OutputResource {
}

// ResourceMetadata returns the BasicResourceProperties of the SecretStore instance.
func (s *SecretStore) ResourceMetadata() *rpv1.BasicResourceProperties {
func (s *SecretStore) ResourceMetadata() rpv1.BasicResourcePropertiesAdapter {
return &s.Properties.BasicResourceProperties
}

Expand Down Expand Up @@ -144,6 +144,6 @@ func (s *SecretStoreListSecrets) OutputResources() []rpv1.OutputResource {
}

// ResourceMetadata returns nil for SecretStoreListSecrets.
func (s *SecretStoreListSecrets) ResourceMetadata() *rpv1.BasicResourceProperties {
func (s *SecretStoreListSecrets) ResourceMetadata() rpv1.BasicResourcePropertiesAdapter {
return nil
}
2 changes: 1 addition & 1 deletion pkg/corerp/datamodel/volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func (h *VolumeResource) OutputResources() []rpv1.OutputResource {
}

// ResourceMetadata returns the BasicResourceProperties of the VolumeResource instance.
func (h *VolumeResource) ResourceMetadata() *rpv1.BasicResourceProperties {
func (h *VolumeResource) ResourceMetadata() rpv1.BasicResourcePropertiesAdapter {
return &h.Properties.BasicResourceProperties
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ func CreateAppScopedNamespace(ctx context.Context, newResource, oldResource *dat

if oldResource != nil {
c := oldResource.Properties.Status.Compute
if c != nil && c.Kind == rpv1.KubernetesComputeKind && c.KubernetesCompute.Namespace != kubeNamespace {
if c.Kind == rpv1.KubernetesComputeKind && c.KubernetesCompute.Namespace != kubeNamespace {
return rest.NewBadRequestResponse(fmt.Sprintf("Updating an application's Kubernetes namespace from '%s' to '%s' requires the application to be deleted and redeployed. Please delete your application and try again.", c.KubernetesCompute.Namespace, kubeNamespace)), nil
}
}
Expand Down
5 changes: 3 additions & 2 deletions pkg/corerp/frontend/controller/secretstores/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ func UpsertSecret(ctx context.Context, newResource, old *datamodel.SecretStore,
}

// resource property cannot be empty for global scoped resource.
if newResource.Properties.BasicResourceProperties.IsGlobalScopedResource() && ref == "" {
if rpv1.IsGlobalScopedResource(&newResource.Properties.BasicResourceProperties) && ref == "" {
return rest.NewBadRequestResponse("$.properties.resource cannot be empty for global scoped resource."), nil
}

Expand Down Expand Up @@ -241,9 +241,10 @@ func UpsertSecret(ctx context.Context, newResource, old *datamodel.SecretStore,
if apierrors.IsNotFound(err) {
// If resource in incoming request references resource, then the resource must exist for a application/environment scoped resource.
// For global scoped resource create the kubernetes resource if not exists.
if ref != "" && !newResource.Properties.BasicResourceProperties.IsGlobalScopedResource() {
if ref != "" && !rpv1.IsGlobalScopedResource(&newResource.Properties.BasicResourceProperties) {
return rest.NewBadRequestResponse(fmt.Sprintf("'%s' referenced resource does not exist.", ref)), nil
}

app, _ := resources.ParseResource(newResource.Properties.Application)
ksecret = &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Expand Down
4 changes: 2 additions & 2 deletions pkg/corerp/setup/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ func SetupNamespace(recipeControllerConfig *controllerconfig.RecipeControllerCon
rp_frontend.PrepareRadiusResource[*datamodel.Extender],
},
AsyncJobController: func(options asyncctrl.Options) (asyncctrl.Controller, error) {
return pr_ctrl.NewCreateOrUpdateResource[*datamodel.Extender, datamodel.Extender](options, &ext_processor.Processor{}, recipeControllerConfig.Engine, recipeControllerConfig.ResourceClient, recipeControllerConfig.ConfigLoader)
return pr_ctrl.NewCreateOrUpdateResource[*datamodel.Extender, datamodel.Extender](options, &ext_processor.Processor{}, recipeControllerConfig.Engine, recipeControllerConfig.ConfigLoader)
},
AsyncOperationTimeout: ext_ctrl.AsyncCreateOrUpdateExtenderTimeout,
AsyncOperationRetryAfter: AsyncOperationRetryAfter,
Expand All @@ -218,7 +218,7 @@ func SetupNamespace(recipeControllerConfig *controllerconfig.RecipeControllerCon
rp_frontend.PrepareRadiusResource[*datamodel.Extender],
},
AsyncJobController: func(options asyncctrl.Options) (asyncctrl.Controller, error) {
return pr_ctrl.NewCreateOrUpdateResource[*datamodel.Extender, datamodel.Extender](options, &ext_processor.Processor{}, recipeControllerConfig.Engine, recipeControllerConfig.ResourceClient, recipeControllerConfig.ConfigLoader)
return pr_ctrl.NewCreateOrUpdateResource[*datamodel.Extender, datamodel.Extender](options, &ext_processor.Processor{}, recipeControllerConfig.Engine, recipeControllerConfig.ConfigLoader)
},
AsyncOperationTimeout: ext_ctrl.AsyncCreateOrUpdateExtenderTimeout,
AsyncOperationRetryAfter: AsyncOperationRetryAfter,
Expand Down
9 changes: 7 additions & 2 deletions pkg/daprrp/datamodel/daprconfigurationstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func (r *DaprConfigurationStore) OutputResources() []rpv1.OutputResource {
}

// ResourceMetadata returns the BasicResourceProperties of the Dapr ConfigurationStore resource i.e. application resources metadata.
func (r *DaprConfigurationStore) ResourceMetadata() *rpv1.BasicResourceProperties {
func (r *DaprConfigurationStore) ResourceMetadata() rpv1.BasicResourcePropertiesAdapter {
return &r.Properties.BasicResourceProperties
}

Expand All @@ -56,13 +56,18 @@ func (r *DaprConfigurationStore) ResourceTypeName() string {
}

// Recipe returns the recipe information of the resource. Returns nil if recipe execution is disabled.
func (r *DaprConfigurationStore) Recipe() *portableresources.ResourceRecipe {
func (r *DaprConfigurationStore) GetRecipe() *portableresources.ResourceRecipe {
if r.Properties.ResourceProvisioning == portableresources.ResourceProvisioningManual {
return nil
}
return &r.Properties.Recipe
}

// SetRecipe sets the recipe information.
func (r *DaprConfigurationStore) SetRecipe(recipe *portableresources.ResourceRecipe) {
r.Properties.Recipe = *recipe
}

// DaprConfigurationStoreProperties represents the properties of Dapr ConfigurationStore resource.
type DaprConfigurationStoreProperties struct {
rpv1.BasicResourceProperties
Expand Down
9 changes: 7 additions & 2 deletions pkg/daprrp/datamodel/daprpubsubbroker.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func (r *DaprPubSubBroker) OutputResources() []rpv1.OutputResource {
}

// ResourceMetadata returns the BasicResourceProperties of the Dapr PubSubBroker resource i.e. application resources metadata.
func (r *DaprPubSubBroker) ResourceMetadata() *rpv1.BasicResourceProperties {
func (r *DaprPubSubBroker) ResourceMetadata() rpv1.BasicResourcePropertiesAdapter {
return &r.Properties.BasicResourceProperties
}

Expand All @@ -56,13 +56,18 @@ func (r *DaprPubSubBroker) ResourceTypeName() string {
}

// Recipe returns the recipe information of the resource. Returns nil if recipe execution is disabled.
func (r *DaprPubSubBroker) Recipe() *portableresources.ResourceRecipe {
func (r *DaprPubSubBroker) GetRecipe() *portableresources.ResourceRecipe {
if r.Properties.ResourceProvisioning == portableresources.ResourceProvisioningManual {
return nil
}
return &r.Properties.Recipe
}

// SetRecipe sets the recipe information.
func (r *DaprPubSubBroker) SetRecipe(recipe *portableresources.ResourceRecipe) {
r.Properties.Recipe = *recipe
}

// DaprPubSubBrokerProperties represents the properties of Dapr PubSubBroker resource.
type DaprPubSubBrokerProperties struct {
rpv1.BasicResourceProperties
Expand Down
9 changes: 7 additions & 2 deletions pkg/daprrp/datamodel/daprsecretstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func (r *DaprSecretStore) OutputResources() []rpv1.OutputResource {
}

// ResourceMetadata returns the BasicResourceProperties of the DaprSecretStore resource i.e. application resources metadata.
func (r *DaprSecretStore) ResourceMetadata() *rpv1.BasicResourceProperties {
func (r *DaprSecretStore) ResourceMetadata() rpv1.BasicResourcePropertiesAdapter {
return &r.Properties.BasicResourceProperties
}

Expand All @@ -68,9 +68,14 @@ type DaprSecretStoreProperties struct {

// Recipe returns the Recipe from the DaprSecretStore Properties if ResourceProvisioning is not set to Manual,
// otherwise it returns nil.
func (r *DaprSecretStore) Recipe() *portableresources.ResourceRecipe {
func (r *DaprSecretStore) GetRecipe() *portableresources.ResourceRecipe {
if r.Properties.ResourceProvisioning == portableresources.ResourceProvisioningManual {
return nil
}
return &r.Properties.Recipe
}

// SetRecipe sets the recipe information.
func (r *DaprSecretStore) SetRecipe(recipe *portableresources.ResourceRecipe) {
r.Properties.Recipe = *recipe
}
9 changes: 7 additions & 2 deletions pkg/daprrp/datamodel/daprstatestore.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func (r *DaprStateStore) OutputResources() []rpv1.OutputResource {
}

// ResourceMetadata returns the BasicResourceProperties of the DaprStateStore resource i.e. application resources metadata.
func (r *DaprStateStore) ResourceMetadata() *rpv1.BasicResourceProperties {
func (r *DaprStateStore) ResourceMetadata() rpv1.BasicResourcePropertiesAdapter {
return &r.Properties.BasicResourceProperties
}

Expand All @@ -56,13 +56,18 @@ func (r *DaprStateStore) ResourceTypeName() string {
}

// Recipe returns the recipe information of the resource. It returns nil if the ResourceProvisioning is set to manual.
func (r *DaprStateStore) Recipe() *portableresources.ResourceRecipe {
func (r *DaprStateStore) GetRecipe() *portableresources.ResourceRecipe {
if r.Properties.ResourceProvisioning == portableresources.ResourceProvisioningManual {
return nil
}
return &r.Properties.Recipe
}

// SetRecipe sets the recipe information.
func (r *DaprStateStore) SetRecipe(recipe *portableresources.ResourceRecipe) {
r.Properties.Recipe = *recipe
}

// DaprStateStoreProperties represents the properties of DaprStateStore resource.
type DaprStateStoreProperties struct {
rpv1.BasicResourceProperties
Expand Down
Loading

0 comments on commit 84c52fd

Please sign in to comment.