forked from radius-project/radius
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial implementation of user-defined-types (radius-project#7686)
This change implements the skeleton of user-defined types. The changes here enable the following: - Users can author a resource of type `System.Resources/resourceProviders` to create a user-defined-type. - Users can use the UCP API to register and query `resourceProviders`. - Users can use the UCP API to execute the full lifecycle of a user-defined-type. Right now the user-defined-type RP will use our default operation (synchronous) controllers to implement the resource lifecycle. There is no background processing. The next step will include the ability to execute asynchronous operations like recipes. - This pull request fixes a bug in Radius and has an approved issue (issue link required). - This pull request adds or changes features of Radius and has an approved issue (issue link required). Part of: radius-project#6688 **note: This change is going into a feature-branch where we can iterate on the user-defined-type design before integrating it with main. The PR is an FYI 😆.** --------- Signed-off-by: ytimocin <[email protected]> Signed-off-by: dependabot[bot] <[email protected]> Signed-off-by: willdavsmith <[email protected]> Signed-off-by: Ryan Nowak <[email protected]> Co-authored-by: Yetkin Timocin <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Will Smith <[email protected]> Signed-off-by: Ryan Nowak <[email protected]>
- Loading branch information
1 parent
100e8cc
commit 8433c6a
Showing
38 changed files
with
1,793 additions
and
84 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
/* | ||
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 api | ||
|
||
// DynamicResource is used as the versioned resource model for dynamic resources. | ||
// | ||
// A dynamic resource is implemented internally to dynamic-rp, and uses a user-provided | ||
// OpenAPI specification to define the resource schema. Since the resource is internal | ||
// and dynamically generated, this struct is used to represent all dynamic resources. | ||
type DynamicResource struct { | ||
ID *string `json:"id"` | ||
Name *string `json:"name"` | ||
Type *string `json:"type"` | ||
Location *string `json:"location"` | ||
Tags map[string]*string `json:"tags,omitempty"` | ||
Properties map[string]any `json:"properties,omitempty"` | ||
SystemData map[string]any `json:"systemData,omitempty"` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
/* | ||
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 api | ||
|
||
import ( | ||
"encoding/json" | ||
|
||
v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" | ||
"github.com/radius-project/radius/pkg/dynamicrp/datamodel" | ||
"github.com/radius-project/radius/pkg/to" | ||
) | ||
|
||
const ( | ||
// TODO | ||
Version = "2023-01-01" | ||
) | ||
|
||
func (d *DynamicResource) ConvertTo() (v1.DataModelInterface, error) { | ||
dm := &datamodel.DynamicResource{ | ||
BaseResource: v1.BaseResource{ | ||
TrackedResource: v1.TrackedResource{ | ||
ID: to.String(d.ID), | ||
Name: to.String(d.Name), | ||
Type: to.String(d.Type), | ||
Location: to.String(d.Location), | ||
Tags: to.StringMap(d.Tags), | ||
}, | ||
InternalMetadata: v1.InternalMetadata{ | ||
UpdatedAPIVersion: Version, | ||
}, | ||
}, | ||
Properties: d.Properties, | ||
} | ||
|
||
return dm, nil | ||
} | ||
|
||
func (d *DynamicResource) ConvertFrom(src v1.DataModelInterface) error { | ||
dm, ok := src.(*datamodel.DynamicResource) | ||
if !ok { | ||
return v1.ErrInvalidModelConversion | ||
} | ||
|
||
d.ID = &dm.ID | ||
d.Name = &dm.Name | ||
d.Type = &dm.Type | ||
d.Location = &dm.Location | ||
d.Tags = *to.StringMapPtr(dm.Tags) | ||
d.SystemData = fromSystemDataDataModel(dm.SystemData) | ||
d.Properties = dm.Properties | ||
if d.Properties == nil { | ||
d.Properties = map[string]any{} | ||
} | ||
d.Properties["provisioningState"] = fromProvisioningStateDataModel(dm.AsyncProvisioningState) | ||
|
||
return nil | ||
} | ||
|
||
func fromSystemDataDataModel(input v1.SystemData) map[string]any { | ||
bs, err := json.Marshal(input) | ||
if err != nil { | ||
// This should never fail. We've designed the SystemData type to be serializable. | ||
panic("marshalling system data failed: " + err.Error()) | ||
} | ||
|
||
result := map[string]any{} | ||
err = json.Unmarshal(bs, &result) | ||
if err != nil { | ||
// This should never fail. We've designed the SystemData type to be serializable. | ||
panic("unmarshalling system data failed: " + err.Error()) | ||
} | ||
|
||
return result | ||
} | ||
|
||
func fromProvisioningStateDataModel(input v1.ProvisioningState) string { | ||
if input == "" { | ||
return string(v1.ProvisioningStateSucceeded) | ||
} | ||
|
||
return string(input) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
/* | ||
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 api | ||
|
||
import ( | ||
"encoding/json" | ||
"testing" | ||
|
||
v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" | ||
"github.com/radius-project/radius/pkg/dynamicrp/datamodel" | ||
"github.com/radius-project/radius/pkg/to" | ||
"github.com/radius-project/radius/test/testutil" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func Test_DynamicResource_ConvertVersionedToDataModel(t *testing.T) { | ||
conversionTests := []struct { | ||
filename string | ||
expected *datamodel.DynamicResource | ||
err error | ||
}{ | ||
{ | ||
filename: "dynamicresource-resource.json", | ||
expected: &datamodel.DynamicResource{ | ||
BaseResource: v1.BaseResource{ | ||
TrackedResource: v1.TrackedResource{ | ||
ID: "/planes/radius/local/resourceGroups/test/providers/Applications.Test/testResources/testResource", | ||
Name: "testResource", | ||
Type: "Applications.Test/testResources", | ||
Location: "global", | ||
Tags: map[string]string{ | ||
"env": "dev", | ||
}, | ||
}, | ||
InternalMetadata: v1.InternalMetadata{ | ||
UpdatedAPIVersion: Version, | ||
}, | ||
}, | ||
Properties: map[string]any{ | ||
"message": "Hello, world!", | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
for _, tt := range conversionTests { | ||
t.Run(tt.filename, func(t *testing.T) { | ||
rawPayload := testutil.ReadFixture(tt.filename) | ||
r := &DynamicResource{} | ||
err := json.Unmarshal(rawPayload, r) | ||
require.NoError(t, err) | ||
|
||
dm, err := r.ConvertTo() | ||
|
||
if tt.err != nil { | ||
require.ErrorIs(t, err, tt.err) | ||
} else { | ||
require.NoError(t, err) | ||
ct := dm.(*datamodel.DynamicResource) | ||
require.Equal(t, tt.expected, ct) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func Test_DynamicResource_ConvertDataModelToVersioned(t *testing.T) { | ||
conversionTests := []struct { | ||
filename string | ||
expected *DynamicResource | ||
err error | ||
}{ | ||
{ | ||
filename: "dynamicresource-datamodel.json", | ||
expected: &DynamicResource{ | ||
ID: to.Ptr("/planes/radius/local/resourceGroups/test/providers/Applications.Test/testResources/testResource"), | ||
Name: to.Ptr("testResource"), | ||
Type: to.Ptr("Applications.Test/testResources"), | ||
Location: to.Ptr("global"), | ||
Tags: map[string]*string{ | ||
"env": to.Ptr("dev"), | ||
}, | ||
Properties: map[string]any{ | ||
"provisioningState": fromProvisioningStateDataModel(v1.ProvisioningStateSucceeded), | ||
"message": "Hello, world!", | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
for _, tt := range conversionTests { | ||
t.Run(tt.filename, func(t *testing.T) { | ||
rawPayload := testutil.ReadFixture(tt.filename) | ||
dm := &datamodel.DynamicResource{} | ||
err := json.Unmarshal(rawPayload, dm) | ||
require.NoError(t, err) | ||
|
||
resource := &DynamicResource{} | ||
err = resource.ConvertFrom(dm) | ||
|
||
// Avoid hardcoding the SystemData field in tests. | ||
tt.expected.SystemData = fromSystemDataDataModel(dm.SystemData) | ||
|
||
if tt.err != nil { | ||
require.ErrorIs(t, err, tt.err) | ||
} else { | ||
require.NoError(t, err) | ||
require.Equal(t, tt.expected, resource) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
{ | ||
"id": "/planes/radius/local/resourceGroups/test/providers/Applications.Test/testResources/testResource", | ||
"name": "testResource", | ||
"type": "Applications.Test/testResources", | ||
"location": "global", | ||
"systemData": { | ||
"createdBy": "[email protected]", | ||
"createdByType": "User", | ||
"createdAt": "2021-09-24T19:09:54.2403864Z", | ||
"lastModifiedBy": "[email protected]", | ||
"lastModifiedByType": "User", | ||
"lastModifiedAt": "2021-09-24T20:09:54.2403864Z" | ||
}, | ||
"tags": { | ||
"env": "dev" | ||
}, | ||
"properties": { | ||
"message": "Hello, world!" | ||
} | ||
} |
Oops, something went wrong.