Skip to content
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

Add ConflictsWith to provider authentication config #1141

Closed
wants to merge 8 commits into from

Conversation

dak1n1
Copy link
Contributor

@dak1n1 dak1n1 commented Feb 3, 2021

Description

In the past, we relied on Kubernetes client-go to determine the order of precedence when given a list of mutually-exclusive parameters for authenticating to the Kubernetes cluster. This lead to some confusion, since client-go's decision could override even an explicit provider configuration. For example, if a token were specified in the Kubernetes provider config, and the environment variable KUBE_CONFIG_PATH was present, client-go would silently decide to use the KUBE_CONFIG_PATH to authenticate to the cluster.

In order to create a more explicit configuration, and more predictable behavior, this PR adds ConflictsWith to specific provider authentication attributes, effectively disallowing the use of conflicting methods of authentication, such as config_path and token. The provider now generates an error to tell the user that these options are mutually exclusive.

Acceptance tests

  • Have you added an acceptance test for the functionality being added?
  • Have you run the acceptance tests on this branch?

Output from acceptance testing:

$ TESTARGS="-run 'TestAccKubernetesProviderConfig'" make testacc
=== RUN   TestAccKubernetesProviderConfig_config_path
--- PASS: TestAccKubernetesProviderConfig_config_path (3.68s)
=== RUN   TestAccKubernetesProviderConfig_config_paths
--- PASS: TestAccKubernetesProviderConfig_config_paths (2.90s)
=== RUN   TestAccKubernetesProviderConfig_config_paths_env
--- PASS: TestAccKubernetesProviderConfig_config_paths_env (2.33s)
=== RUN   TestAccKubernetesProviderConfig_config_paths_env_wantError
--- PASS: TestAccKubernetesProviderConfig_config_paths_env_wantError (0.43s)
=== RUN   TestAccKubernetesProviderConfig_host_env_wantError
--- PASS: TestAccKubernetesProviderConfig_host_env_wantError (0.55s)
=== RUN   TestAccKubernetesProviderConfig_host
--- PASS: TestAccKubernetesProviderConfig_host (8.68s)
PASS

Release Note

Release note for CHANGELOG:

Display an error when the provider is configured with mutually-exclusive authentication options.

References

Community Note

  • Please vote on this issue by adding a 👍 reaction to the original issue to help the community and maintainers prioritize this request
  • If you are interested in working on this issue or have submitted a pull request, please leave a comment

command = "aws"
}
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved the provider blocks into the root module, since that's best practice.

token = data.google_client_config.default.access_token
cluster_ca_certificate = base64decode(var.cluster_ca_cert)
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved this provider config into the root module.

# This can be set statically, if preferred. See docs for details.
# https://registry.terraform.io/providers/hashicorp/google/latest/docs/guides/provider_reference#full-reference
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This provider config was moved into the root module.

@dak1n1 dak1n1 force-pushed the conflictswith branch 2 times, most recently from 002dc63 to 72a16df Compare February 20, 2021 00:00
if err := checkConfigurationValid(k.configData); err != nil {
return nil, err
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did a bunch of Progressive Apply testing and I wasn't able to find a difference between having this check enabled vs disabled.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you do your testing across different versions of Terraform and can you share your testing strategy? I'm still pretty uncertain about reliably reproducing the illusive progressive apply issue.

You can look at the issue that caused us to revert this change in the Helm provider for context: hashicorp/terraform-provider-helm#647

} else if v := os.Getenv("KUBE_CONFIG_PATHS"); v != "" {
// NOTE we have to do this here because the schema
// does not yet allow you to set a default for a TypeList
configPaths = filepath.SplitList(v)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SDK now seems to allow us to set a default for TypeList! 🎉 I didn't see the feature added, but it worked for me when I defined my own function of type schema.EnvDefaultFunc.

@@ -334,16 +434,16 @@ func initializeConfiguration(d *schema.ResourceData) (*restclient.Config, error)
}

// Overriding with static configuration
if v, ok := d.GetOk("insecure"); ok {
if v, ok := d.GetOk("insecure"); ok && v != "" {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These checks are just meant to ensure the various configuration attributes are not initialized with any default/empty values. For example, if the provider thinks we've set host = "", it won't let us set config_path = "~/.kube/config" because of the ConflictsWith.

@dak1n1 dak1n1 linked an issue Feb 25, 2021 that may be closed by this pull request
@ghost ghost added size/XXL and removed size/XL labels Feb 26, 2021
Copy link
Contributor

@jrhouston jrhouston left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for doing this @dak1n1 – this mostly looks great, especially all the tests you've added and config examples you've tidied up.

I added a few questions about the conflicts with stuff, and there seems to be a problem with sourcing the KUBE_CONFIG_PATHS environment variable.

Optional: true,
Description: "A list of paths to kube config files. Can be set with KUBE_CONFIG_PATHS environment variable.",
// config_paths conflicts with every attribute except for "insecure", since all of these options will be read from the kubeconfig.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like the insecure option can actually be part of the kubeconfig file but is called insecure-skip-tls-verify https://github.com/kubernetes/kubernetes/blob/f4a156eb29d51b73f52d69ab4c5f96e440eebc41/staging/src/k8s.io/client-go/pkg/apis/clientauthentication/v1beta1/types.go#L86

Is there a good reason to also have it here when using config_path in that case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, my mistake! I thought it was a command-line override that works anywhere. I'll have to fix that. But I'm going to hold off doing anything with this PR until we have that meeting next week.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to give you a status update: I'll fix the things you mentioned as soon as I can get the needed functionality into the SDK. (Since we don't want to enable ConflictsWith in "fatal error" mode, the SDK work will be needed before this PR can progress).

Optional: true,
DefaultFunc: schema.EnvDefaultFunc("KUBE_CTX", nil),
Description: "Context to choose from the kube config file. ",
ConflictsWith: []string{"exec", "token", "client_certificate", "client_key", "username", "password"},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't this also conflict with cluster_ca_certificate since it's just for when using a kubeconfig file?

// configPathsEnv fetches the value of the environment variable KUBE_CONFIG_PATHS, if defined.
func configPathsEnv() (interface{}, error) {
value, exists := os.LookupEnv("KUBE_CONFIG_PATHS")
if exists {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a nit but because there's a bunch of stuff inside this conditional i would invert it so that if !exists returns early so the rest of the logic is just the body of the function.

},
"config_paths": {
Type: schema.TypeList,
Elem: &schema.Schema{Type: schema.TypeString},
DefaultFunc: configPathsEnv,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't actually seem to be working for me on your branch:

When I use KUBE_CONFIG_PATH it works fine:


KUBE_CONFIG_PATH=~/.kube/config terraform plan                              ✘ 1 conflictswith ✱ ◼
kubernetes_config_map.testy: Refreshing state... [id=default/testo]

No changes. Infrastructure is up-to-date.

That Terraform did not detect any differences between your configuration and the remote system(s). As a result, there
are no actions to take.

But when I try to use KUBE_CONFIG_PATHS:

KUBE_CONFIG_PATHS=~/.kube/config terraform plan                             ✘ 1 conflictswith ✱ ◼
kubernetes_config_map.testy: Refreshing state... [id=default/testo]
╷
│ Error: Get "http://localhost/api/v1/namespaces/default/configmaps/testo": dial tcp [::1]:80: connect: connection refused
│
│
╵

However if I set config_paths in the config it works fine. So I suspect something isn't right with the default function here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, thanks for testing! I'll have to check this out.

if err := checkConfigurationValid(k.configData); err != nil {
return nil, err
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you do your testing across different versions of Terraform and can you share your testing strategy? I'm still pretty uncertain about reliably reproducing the illusive progressive apply issue.

You can look at the issue that caused us to revert this change in the Helm provider for context: hashicorp/terraform-provider-helm#647

})
}

func TestAccKubernetesProviderConfig_config_paths_env(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that I couldn't get KUBE_CONFIG_PATHS to work locally as mentioned above, I'm confused by how this test passes 🤯

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see this probably passes because the validation on the provider block doesn't error but terraform breaks gives an error at apply time because the list is empty.

I added some logging to the provider block where I can see that configPathsEnv is being called, but when I do a d.Get("config_paths") in the providerConfigure function it comes up empty. 😢

},
{
Config: testAccKubernetesProviderConfig(
providerConfig_host("https://test-host") +
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found the nesting of functions here a bit confusing to read – I think I would have preferred just one big string with the provider block in it so I can see exactly what is being tested. I see you are trying not to repeat yourself here and avoid copypasta though.

@dak1n1
Copy link
Contributor Author

dak1n1 commented Apr 21, 2021

There's a PR that I tested out today, which gives us the conflictsWith warning level messages. It's working, but it will take some time for it to be reviewed and released. Probably sometime in June. hashicorp/terraform-plugin-sdk#745

In order to create a more explicit configuration, and more predictable behavior, this PR adds ConflictsWith to specific provider authentication attributes, effectively disallowing the use of conflicting methods of authentication, such as config_path and token. The provider now generates an error to tell the user that these options are mutually exclusive.
@hashicorp-cla
Copy link

hashicorp-cla commented Mar 12, 2022

CLA assistant check
All committers have signed the CLA.

@BBBmau BBBmau requested a review from a team as a code owner April 11, 2023 16:01
@BBBmau BBBmau mentioned this pull request May 3, 2023
2 tasks
@jrhouston jrhouston closed this Jun 6, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Provider allows mutually-exclusive configuration options
4 participants