-
Notifications
You must be signed in to change notification settings - Fork 372
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
feat(servicecatalog): Add provisioned product #1806
feat(servicecatalog): Add provisioned product #1806
Conversation
7f410e4
to
2577615
Compare
Hi @MisterMX! Can you please check this PR and give us your first impression? Thanks a lot! |
3945a36
to
9b399a3
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for your contribution @teeverr. I added some remarks to the code. Can you take a look at them? Thanks!
case ppStatus == string(svcapitypes.ProvisionedProductStatus_SDK_UNDER_CHANGE) && *describeRecordOutput.RecordDetail.RecordType == "UPDATE_PROVISIONED_PRODUCT": | ||
cr.SetConditions(xpv1.Available().WithMessage("Provisioned Product is updating, availability depends on product")) | ||
case ppStatus == string(svcapitypes.ProvisionedProductStatus_SDK_ERROR): | ||
cr.Status.SetConditions(xpv1.ReconcileError(errors.New(errProvisionedProductStatusSdkError))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not a reconcile error. It only indicates a misconfiguration of the external resource. Setting ReconcileError
would send the wrong message to the user (and might be overwritten by the runtime anyways). The Unavailable
status condition is should be used here:
cr.Status.SetConditions(xpv1.ReconcileError(errors.New(errProvisionedProductStatusSdkError))) | |
cr.Status.SetConditions(xpv1.Unvailable().WithMessage(errProvisionedProductStatusSdkError)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is true if provisioned product has Tainted state, but if it has Error update is not allowed, so crossplane will never reconcile that resource
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want to prevent an update you can look up the state during Update
and do nothing if it is not allowed - or you return ResourceIsUpdToDate: true
in Observe
. Either way, setting the ReconcileError
condition here is still not correct and you should use Unvailable
.
413df22
to
0919706
Compare
Thanks for review @MisterMX! I hope I fulfilled all of your remarks : ) |
0919706
to
0bd9555
Compare
34c9607
to
e0cd859
Compare
case ppStatus == string(svcapitypes.ProvisionedProductStatus_SDK_UNDER_CHANGE) && *describeRecordOutput.RecordDetail.RecordType == "UPDATE_PROVISIONED_PRODUCT": | ||
cr.SetConditions(xpv1.Available().WithMessage("Provisioned Product is updating, availability depends on product")) | ||
case ppStatus == string(svcapitypes.ProvisionedProductStatus_SDK_ERROR): | ||
cr.Status.SetConditions(xpv1.ReconcileError(errors.New(errProvisionedProductStatusSdkError))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want to prevent an update you can look up the state during Update
and do nothing if it is not allowed - or you return ResourceIsUpdToDate: true
in Observe
. Either way, setting the ReconcileError
condition here is still not correct and you should use Unvailable
.
if err != nil { | ||
return "", err | ||
} | ||
return *output.ProvisioningArtifactDetail.Id, nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You might run into a panic when dereferencing a nil value. Better to check if it is nil before returning it.
|
||
cfStackKeyValue := make(map[string]string) | ||
for _, v := range cfStackParams { | ||
cfStackKeyValue[*v.ParameterKey] = *v.ParameterValue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Possible nil deref. Better use pointer.StringDeref
.
Thanks for the changes @teeverr. There are some minor issues that need to be solved before merging. Can you take a look at them? Also, can you squash your changes into a single commit and sign it? |
Thanks for the review! I work on the team handling this on Swisscom's side, and we should have an updated PR ready soon that incorporates this feedback, and fixes several bugs and issues we've discovered so far. |
c70dd56
to
3be0213
Compare
@MisterMX I've pushed a new version of this work, which addresses the feedback you provided as well as resolving the following discovered issues:
As well, I did some refactoring to try to clean things up a bit and keep it organized. |
…ervice Catalog Signed-off-by: Kai Parry <[email protected]>
3be0213
to
4482809
Compare
Fixed the linter errors, apologies. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your changes @threadproc
I left some comments here, the summary(and concern) of which is to understand that all of these 'refactoring' are really necessary and improve the PR and these are not changes for changes.
Because it would be more than enough to add 10-20 simple lines of code to fix 3-4 bugs.
Ps. It would be really nice to create new PR to this branch before. Because after squashing is happend it became hardly understandable, but I restored the original branch from my local backup. So I used this link in my comments/remarks for comparison and clarification
reconcilerOpts...)) | ||
} | ||
|
||
func (c *customConnector) Connect(ctx context.Context, mg resource.Managed) (managed.ExternalClient, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the advantage of this func in comparison of prepareSetupExternal
from the original branch
https://github.com/swisscom/provider-aws/pull/1/conflicts
@@ -0,0 +1,194 @@ | |||
package provisionedproduct |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@threadproc
Before squashing this branch had the same stucture for provisioned product which already used by others 100+ resources in this provider. Are sure it's necessary to have this "lifecycle.go"?
@@ -0,0 +1,101 @@ | |||
package provisionedproduct |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@threadproc
Original PR had the same stucture for provisioned product which already used by others 100+ resources in this provider. Are sure it's necessary to use this "utils.go"?
case ppStatus == string(svcapitypes.ProvisionedProductStatus_SDK_AVAILABLE): | ||
cr.SetConditions(xpv1.Available()) | ||
case ppStatus == string(svcapitypes.ProvisionedProductStatus_SDK_UNDER_CHANGE) && recordType == "PROVISION_PRODUCT": | ||
cr.SetConditions(xpv1.Creating()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But this condition already set in zz_controller.go, it is necessary here?
cr.SetConditions(xpv1.Creating()) | ||
case ppStatus == string(svcapitypes.ProvisionedProductStatus_SDK_UNDER_CHANGE) && recordType == "UPDATE_PROVISIONED_PRODUCT": | ||
cr.SetConditions(xpv1.Unavailable().WithMessage(msgProvisionedProductStatusSdkUnderChange)) | ||
case ppStatus == string(svcapitypes.ProvisionedProductStatus_SDK_UNDER_CHANGE) && recordType == "TERMINATE_PROVISIONED_PRODUCT": |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But this condition already set in zz_controller.go, it is necessary here?
func (c *custom) productOrArtifactAreChanged(ds *svcapitypes.ProvisionedProductParameters, resp *svcsdk.ProvisionedProductDetail) (bool, error) { // nolint:gocyclo | ||
var productID, artifactID string | ||
|
||
if ds.ProductID != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this if is redundant
if ds.ProductID != nil { | ||
productID = pointer.StringDeref(ds.ProductID, "") | ||
} | ||
if ds.ProvisioningArtifactID != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this if is redundant
} | ||
} | ||
|
||
return pointer.StringDeref(resp.ProductId, "") != productID || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These fields can't be nil, so deref is redundant. But as I understand @MisterMX recommends to deref any pointers, idk
Another question about this func in general... as I wrote above.
productID = pointer.StringDeref(describeProduct.ProductViewSummary.ProductId, "") | ||
} | ||
|
||
if artifactID == "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
while?
} | ||
|
||
// productOrArtifactAreChanged will attempt to determine whether or not the currently requested SC product and provisioning artifact IDs have changed when using names instead of IDs | ||
func (c *custom) productOrArtifactAreChanged(ds *svcapitypes.ProvisionedProductParameters, resp *svcsdk.ProvisionedProductDetail) (bool, error) { // nolint:gocyclo |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// nolint:gocyclo
I think the idea of this rule is to keep code clean and protect it from some things... : )
|
||
if artifactID == "" { | ||
// find the matching artifact | ||
for _, artifact := range describeProduct.ProvisioningArtifacts { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
O(n) here, but O(1) in original branch with another method(DescribeProvisioningArtifact). Why?
// API call. | ||
dpInput := &svcsdk.DescribeProductInput{} | ||
|
||
if productID != "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These two field are mutualy exclusive, it means this if is redundant again and even more it follows to bugs or unpected behavior, cuz if user set both he/she don't get relevant error, cuz you use productid every time.
And again the question arises what was wrong with the original code/func?
Hi @MisterMX Thanks a lot for your reviews and feedbacks! We still run internal tests with this service catalog feature of provider-aws and we might still hit some bugs. I propose to keep this PR open/draft until we're done with the testing phase. I'll keep you updated! Thank you again! Wish a nice day! Marius |
This PR has been replaced by this - #1908 |
Description of your changes
Adds new resource - ProvisionedProduct, which represents life cycle of launched product in service catalog
I have:
make reviewable test
to ensure this PR is ready for review.How has this code been tested
Many scenarios have been tested manually In development env and also I wrote some unit tests for custom controller logic