-
Notifications
You must be signed in to change notification settings - Fork 38
Conversation
@muvaf would this mean, users no longer reliably use Terraform import id as an If we chose to still use Terraform import ids as external names, I believe we could cover all cases with two functions as below:
I think this would mean us not marking some (required) fields as not required so that they can be late initialized during import. |
c970efb
to
ba2a5cd
Compare
Synced with @turkenh about his proposal and we decided not to proceed with it because it'd mean to make external name same as ID which is not acceptable in some cases especially in composition where you may not have some parts of the information stored in the ID, such as subscriptionID in Azure. @ulucinar @turkenh I added your suggestion about the provider config content and opened crossplane-contrib/provider-jet-azure#84 to show how it all would look like. |
…nversions Signed-off-by: Muvaffak Onus <me@muvaf.com>
…Terraformed interface Signed-off-by: Muvaffak Onus <me@muvaf.com>
… flow now Signed-off-by: Muvaffak Onus <me@muvaf.com>
…f the id to handle cases where project, region, subscription id etc. is included in the id Signed-off-by: Muvaffak Onus <me@muvaf.com>
Signed-off-by: Muvaffak Onus <me@muvaf.com>
pkg/resource/sensitive.go
Outdated
// just a nice-to-have, hence we don't handle cases where the ID is given | ||
// in a field other than "id". cfg.ExternalName.GetNameFn wouldn't really | ||
// help because we need the raw ID. | ||
if id, ok := attr["id"]; conn != nil && ok { |
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.
Should we consider dropping including id
to connection secrets?
I remember we've added this to support cases where id
field is required as a connection detail, e.g. id in aws_iam_access_key. But now we have a way to include any information from the tf schema as a conn detail with AdditionalConnectionDetailsFn
.
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.
Looks like we pass Terraform attribute map to AdditionalConnectionDetailsFn
. id
might incorporate addition info, such as the subscription id for Azure resources, which may not be available in any other attributes. We should consider this in the discussion.
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.
@ulucinar The attribute map we pass to AdditionalConnectionDetailsFn
is the full TFState, which is where we pull the id
from in this logic. So it should be available in that map. Maybe we can rename the argument name of that function from attr
to tfstate
to make it more clear.
@turkenh I thought about removing it but then ended up publishing it only if connection detail secret has some content so that we don't publish a connection secret for every resource, thinking that id
of RDS Instance could be helpful for connecting for example but I do expect developers to write an AdditionalConnectionDetailsFn
for connectible resources in all cases anyway. I'll just remove it.
pkg/config/resource.go
Outdated
} | ||
|
||
// GetNameFn returns the external name extracted from the TF State. | ||
type GetNameFn func(tfstate map[string]interface{}) string |
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.
I believe returning errors in these functions would be helpful for debugging by making what went wrong more explicit, i.e. instead returning an empty string.
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.
:) I have similar concerns here as I detailed above.
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.
Added it. I also added ctx
to GetIDFn
so that we make queries there, like getting account id by making get-caller-identity
call in AWS to construct the full ARN.
pkg/config/resource.go
Outdated
|
||
// GetIDFn returns the ID to be used in TF State file, i.e. "id" field in | ||
// terraform.tfstate. | ||
type GetIDFn func(name string, parameters map[string]interface{}, providerConfig map[string]interface{}) string |
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.
I was thinking of the providerConfig
map as a more generalized key-value map from which specific implementations can extract the needed information, i.e., a "producer" puts key,value pairs to be consumed by a specific instance of the GetIDFn
via known keys. I think we currently augment the context only using the ProviderConfig
s so the parameter is named as providerConfig
. We may rename it with something like configMap
so that it conveys a more generalized meaning.
Also here my understanding is that an error case (probably a programming error case in which a known key is not put into this map and the consumer, GetIDFn
function instance, cannot construct the ID) signals it by returning an empty string. This will cause FileProducer.WriteTFState
to set the generated tfstate's id
attribute to an empty string. How does Terraform behave in this case? If Terraform errors in the above case, it's a better scenario but the error message will be indirect, i.e., instead of stating that an expected key could not be found in a configuration map, we might observe something like "resource not exists", or similar. Or, does it behave in undesired ways? Do we have an observation for this situation? It makes sense to detect and properly report a missing piece of expected information while calculating the id
attribute via an error
. Thus we may consider returning an error here and breaking early before running any Terraform commands in FileProducer.WriteTFState
.
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.
i.e., a "producer" puts key,value pairs to be consumed by a specific instance of the GetIDFn via known keys.
Could you elaborate? Right now, we can only give generic information to this function like those given, and if users want to add more they can do that in runtime by returning anonymous function that take value from somewhere else.
Thus we may consider returning an error here and breaking early before running any Terraform commands in FileProducer.WriteTFState.
Thanks, added.
pkg/config/resource.go
Outdated
type GetIDFn func(name string, parameters map[string]interface{}, providerConfig map[string]interface{}) string | ||
|
||
// NameAsID returns the name to be used as ID in TF State file. | ||
var NameAsID GetIDFn = func(name string, _ map[string]interface{}, _ map[string]interface{}) string { |
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.
nit: Type specification in the initializer is redundant.
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.
I added them on purpose actually so that we get compilation errors here right away if they don't adhere to those types. Otherwise, they'd remain as anonymous functions until used somewhere to return compile error.
pkg/config/resource.go
Outdated
} | ||
|
||
// GetNameFn returns the external name extracted from the TF State. | ||
type GetNameFn func(tfstate map[string]interface{}) string |
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.
:) I have similar concerns here as I detailed above.
pkg/config/resource.go
Outdated
|
||
// IDAsName returns the TF State ID as external name. | ||
var IDAsName GetNameFn = func(tfstate map[string]interface{}) string { | ||
if id, ok := tfstate["id"].(string); ok { |
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.
nit: The ok
bool checks the type assertion here, i.e., the string
assertion. We will be returning an empty string if id
does not exist, or the map value is not of the expected type. This is not an issue under our current assumptions, which may be broken in the future due to bugs or breaking changes. This is related to the above discussion on error reporting. It may prove to be useful to differentiate missing keys and unexpected value types here.
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.
Added error reporting and made sure we return error if id
is ""
|
||
// GetIDFn returns the string that will be used as "id" key in TF state. In | ||
// most cases, it should return given external name as is. | ||
GetIDFn GetIDFn |
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.
nit: The immediate context here is (the enclosing type) the ExternalName
struct, which is a Crossplane concept. I think it will be more explicit to rename this field as GetTerraformIDFn
.
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.
I'd actually prefer to keep it as ID since that's what it's called in the map that this function processes, similarly how ExternalName
corresponds to crossplane.io/external-name
annotation.
pkg/resource/lateinit.go
Outdated
@@ -53,7 +54,7 @@ type GenericLateInitializer struct { | |||
} | |||
|
|||
// LateInitializeAnnotations late initializes annotations of the resource | |||
func LateInitializeAnnotations(tr Terraformed, attr map[string]interface{}, privateRaw string) (bool, error) { | |||
func LateInitializeAnnotations(tr metav1.Object, name string, privateRaw string) (bool, 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.
nit: As mentioned above in one of @turkenh's comments, it makes sense to rename name
parameter as externalName
to make it more explicit.
if err != nil { | ||
return nil, errors.Wrap(err, "cannot get connection details") | ||
} | ||
|
||
var custom map[string][]byte | ||
// TODO(turkenh): Once we have automatic defaulting, remove this if check. |
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.
Is the idea here that we will always have a non-zero AdditionalConnectionDetailsFn
because of the defaulting? I would still prefer to keep this check so that a nil AdditionalConnectionDetailsFn
continues to behave as a noop.
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.
See my comment in #132 (comment)
pkg/resource/sensitive.go
Outdated
// just a nice-to-have, hence we don't handle cases where the ID is given | ||
// in a field other than "id". cfg.ExternalName.GetNameFn wouldn't really | ||
// help because we need the raw ID. | ||
if id, ok := attr["id"]; conn != nil && ok { |
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.
Looks like we pass Terraform attribute map to AdditionalConnectionDetailsFn
. id
might incorporate addition info, such as the subscription id for Azure resources, which may not be available in any other attributes. We should consider this in the discussion.
if fp.Config.ExternalName.SetIdentifierArgumentFn != nil { | ||
fp.Config.ExternalName.SetIdentifierArgumentFn(params, meta.GetExternalName(tr)) | ||
} | ||
fp.Config.ExternalName.SetIdentifierArgumentFn(params, meta.GetExternalName(tr)) |
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.
I think this was a common pattern we wanted to switch to after defaulting. Please see the above comment.
…ecret since it is already available to be added by additional connections fn Signed-off-by: Muvaffak Onus <me@muvaf.com>
…ample cases Signed-off-by: Muvaffak Onus <me@muvaf.com>
Signed-off-by: Muvaffak Onus <me@muvaf.com>
Signed-off-by: Muvaffak Onus <me@muvaf.com>
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.
LGTM, thanks @muvaf !
…to be made, it can be done. For example, account ID in AWS to construct the full ARN Signed-off-by: Muvaffak Onus <me@muvaf.com>
Description of your changes
Allows users to override the default
TF State ID -> External Name
andExternal Name -> TF State ID
flows. Note that we already haveSetIdentifierArgumentFn
endpoint but that's for overridingExternal Name -> Main TF File
. Details about why we're doing this are in the issue.Depends on #128 . See the last 3 commits for actual diff.
Fixes #119
Fixes #104
I have:
make reviewable
to ensure this PR is ready for review.backport release-x.y
labels to auto-backport this PR if necessary.How has this code been tested
Unit tests and see crossplane-contrib/provider-jet-azure#84