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

[Federation] Use service accounts instead of the user's credentials when accessing joined clusters' API servers. #42042

Merged
merged 1 commit into from
May 24, 2017

Conversation

perotinus
Copy link
Contributor

@perotinus perotinus commented Feb 24, 2017

Fixes #41267.

Release notes:

Modifies kubefed to create and the federation controller manager to use credentials associated with a service account rather than the user's credentials.

@k8s-ci-robot
Copy link
Contributor

Hi @perotinus. Thanks for your PR.

I'm waiting for a kubernetes member to verify that this patch is reasonable to test. If it is, they should reply with @k8s-bot ok to test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should join the org to skip this step.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed here.

@k8s-ci-robot k8s-ci-robot added the cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. label Feb 24, 2017
@k8s-reviewable
Copy link

This change is Reviewable

@perotinus
Copy link
Contributor Author

cc @madhusudancs, @nikhiljindal

@k8s-github-robot k8s-github-robot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. release-note Denotes a PR that will be considered when it comes time to generate release notes. labels Feb 24, 2017
@madhusudancs
Copy link
Contributor

@irfanurrehman can you review this? I will also try to do a pass.

@irfanurrehman
Copy link
Contributor

@madhusudancs, yes, certainly!

@irfanurrehman
Copy link
Contributor

Review status: 0 of 6 files reviewed at latest revision, 26 unresolved discussions.


federation/cluster/federation-up.sh, line 98 at r1 (raw file):

        --host-cluster-context="${HOST_CLUSTER_CONTEXT}" \
        --context="${FEDERATION_NAME}" \
        --secret-name="${context//_/-}" -v=2    # Replace "_" by "-"

Any specific reason for this change?
I think the intention of --v=2 is different (for example intended for additional information from the tool or the libs the tool is using)
When federation-up.sh prints such info, it might surprise users (for example, an info I get from kubefed unjoin at this verbosity is details about all flags passed to kubefed join, which the federation-up.sh user is unaware of).
Other point of view might be why do this only for join, why not for init, or unjoin.. ?


federation/pkg/federation-controller/util/cluster_util.go, line 69 at r1 (raw file):

			clusterConfig, err = clientcmd.BuildConfigFromFlags(serverAddress, "")
		} else {
			kubeconfigGetter := KubeconfigGetterForCluster(c)

you can as well pass the secret to this function, which u already have here.
It will avoid the getSecret() call twice.


federation/pkg/federation-controller/util/cluster_util.go, line 72 at r1 (raw file):

			if err != nil {
				return nil, err
			}

you return nil, nil in case SecretRef.Name == nil from GetSecret() , which unnecessarily traces the rest of the code.

	if secretName == "" {
		return nil, nil
	}

You can as well format an error in return from GetSecret() or handle the scenario here itself, with this check not needed inside GetSecret()


federation/pkg/federation-controller/util/cluster_util.go, line 78 at r1 (raw file):

			// serialized kubeconfig.
			if _, ok := secret.Data["token"]; ok {
				clusterConfig, err = clientcmd.BuildConfigFromFlags(serverAddress, "")

Assigning to clusterConfig without verifying error, clusterConfig could be null?


federation/pkg/federation-controller/util/cluster_util.go, line 97 at r1 (raw file):

// This is to inject a different kubeconfigGetter in tests.
// We don't use the standard one which calls NewInCluster in tests to avoid having to setup service accounts and mount files with secret tokens.
var KubeconfigGetterForCluster = func(c *federation_v1beta1.Cluster) clientcmd.KubeconfigGetter {

U can as well pass the secret object here, rather then the cluster object, because u already have it earlier.
Or even secret.Data, because that is what you consume in the getter().
Also, there wont be any need of nested functions GetterForCluster() and then GetterForSecret(), one function would suffice.


federation/pkg/federation-controller/util/cluster_util.go, line 100 at r1 (raw file):

	return func() (*clientcmdapi.Config, error) {
		secretRefName := ""
		if c.Spec.SecretRef != nil {

you sort of have already done this check earlier (in BuildClusterConfig at line 65), and then call KubeconfigGetterForCluster(), so probably not needed here again.


federation/pkg/federation-controller/util/cluster_util.go, line 109 at r1 (raw file):

}

// GetterForSecret is used to get a secret from the cluster. It is used to inject a different implementation in tests.

s/GetterForSecret/GetSecret
is this statement true or a copy paste mistake ->> "It is used to inject a different implementation in tests" ?


federation/pkg/federation-controller/util/cluster_util.go, line 110 at r1 (raw file):

// GetterForSecret is used to get a secret from the cluster. It is used to inject a different implementation in tests.
func GetSecret(secretName string) (*api.Secret, error) {

You need not export the function if used only here.


federation/pkg/federation-controller/util/cluster_util.go, line 148 at r1 (raw file):

// KubeconfigGetterForSecret gets the kubeconfig from the given secret.
func KubeconfigGetterForSecret(secretName string) clientcmd.KubeconfigGetter {

As in the comment on line 97, you probably don't need the nested function.


federation/pkg/kubefed/join.go, line 148 at r1 (raw file):

	}

	// TODO: This needs to check if the API server for the joining cluster supports RBAC.

I think this might need implementation now, rather then a TODO, otherwise registering older version clusters would fail, which works now and we do not want to break that.


federation/pkg/kubefed/join.go, line 150 at r1 (raw file):

	// TODO: This needs to check if the API server for the joining cluster supports RBAC.
	// If the cluster does not, this should fall back to the old secret creation strategy.
	glog.V(2).Info("Creating federation system namespace")

The info message ideally should be complete because we create somethings in target cluster and somethings in base cluster.
Creating federation system namespace in the joining cluster



---

*[federation/pkg/kubefed/join.go, line 151 at r1](https://reviewable.kubernetes.io:443/reviews/kubernetes/kubernetes/42042#-KdlJD3DpKYH0yyXPjAX:-KdlJD3DpKYH0yyXPjAY:b-gvyatx) ([raw file](https://github.com/kubernetes/kubernetes/blob/e9ab6462d4b5844b617aac6de42bb602895c0b52/federation/pkg/kubefed/join.go#L151)):*
> ```Go
> 	// If the cluster does not, this should fall back to the old secret creation strategy.
> 	glog.V(2).Info("Creating federation system namespace")
> 	err = createFederationSystemNamespace(joiningClusterClient, j.commonOptions.FederationSystemNamespace)
> ```

You need to use the dryRun flag and ensure not to create object in the target cluster if dryRun is true.

---

*[federation/pkg/kubefed/join.go, line 153 at r1](https://reviewable.kubernetes.io:443/reviews/kubernetes/kubernetes/42042#-KdlHUocgAZ69HHTH4S7:-KdlHUod_Qbuwo6e9m_r:bcpobnc) ([raw file](https://github.com/kubernetes/kubernetes/blob/e9ab6462d4b5844b617aac6de42bb602895c0b52/federation/pkg/kubefed/join.go#L153)):*
> ```Go
> 	err = createFederationSystemNamespace(joiningClusterClient, j.commonOptions.FederationSystemNamespace)
> 	if err != nil {
> 		glog.V(2).Info("Error creating federation system namespace: %v", err)
> ```

Same as previous comment about the info message (and please apply it to all subsequent info messages)

---

*[federation/pkg/kubefed/join.go, line 158 at r1](https://reviewable.kubernetes.io:443/reviews/kubernetes/kubernetes/42042#-KdlJQt6H49yjrArH1-h:-KdlJQt6H49yjrArH1-i:bnbhlri) ([raw file](https://github.com/kubernetes/kubernetes/blob/e9ab6462d4b5844b617aac6de42bb602895c0b52/federation/pkg/kubefed/join.go#L158)):*
> ```Go
> 
> 	glog.V(2).Info("Creating service account")
> 	saName, err := createServiceAccount(joiningClusterClient, j.commonOptions.FederationSystemNamespace, j.commonOptions.Host)
> ```

Same comment for the use of dryRun, and please apply the same to all subsequent functions.

---

*[federation/pkg/kubefed/join.go, line 249 at r1](https://reviewable.kubernetes.io:443/reviews/kubernetes/kubernetes/42042#-KdlKLfS3BGt44rrDvwZ:-KdlKLfS3BGt44rrDvw_:bgiaf89) ([raw file](https://github.com/kubernetes/kubernetes/blob/e9ab6462d4b5844b617aac6de42bb602895c0b52/federation/pkg/kubefed/join.go#L249)):*
> doesn't exist
doesn't already exist

---

*[federation/pkg/kubefed/join.go, line 259 at r1](https://reviewable.kubernetes.io:443/reviews/kubernetes/kubernetes/42042#-KdlJnncbAZfwhcX6PAK:-KdlJnncbAZfwhcX6PAL:bs59dp0) ([raw file](https://github.com/kubernetes/kubernetes/blob/e9ab6462d4b5844b617aac6de42bb602895c0b52/federation/pkg/kubefed/join.go#L259)):*
> Could not create federation-system namespace in client
Could not create federation-system namespace in joining cluster.

---

*[federation/pkg/kubefed/join.go, line 265 at r1](https://reviewable.kubernetes.io:443/reviews/kubernetes/kubernetes/42042#-KdlK9qy64QT0NgN4yww:-KdlK9qy64QT0NgN4ywx:b-xu9nt4) ([raw file](https://github.com/kubernetes/kubernetes/blob/e9ab6462d4b5844b617aac6de42bb602895c0b52/federation/pkg/kubefed/join.go#L265)):*
> ```Go
> }
> 
> // createServiceAccount creates a service account in the cluster associated with clusterClienr with
> ```

s/clusterClienr/clusterClient

---

*[federation/pkg/kubefed/join.go, line 268 at r1](https://reviewable.kubernetes.io:443/reviews/kubernetes/kubernetes/42042#-KdlL_6w3eOxfKw9yjRJ:-KdlL_6w3eOxfKw9yjRK:bqu53jl) ([raw file](https://github.com/kubernetes/kubernetes/blob/e9ab6462d4b5844b617aac6de42bb602895c0b52/federation/pkg/kubefed/join.go#L268)):*
> ```Go
> // credentials that will be used by the host cluster to access its API server.
> func createServiceAccount(clusterClient *internalclientset.Clientset, namespace, hostContext string) (string, error) {
> 	saName := util.ClusterServiceAccountName(hostContext)
> ```

Because the same cluster can join multiple federations (with a possibility that 2 admins might use a same context name for the cluster), I think we should contemplate getting a more unique name for the service account.

---

*[federation/pkg/kubefed/join.go, line 277 at r1](https://reviewable.kubernetes.io:443/reviews/kubernetes/kubernetes/42042#-KdlM7434_SFtXuz1vvN:-KdlM744qi73c2WBU3AU:b-bawas6) ([raw file](https://github.com/kubernetes/kubernetes/blob/e9ab6462d4b5844b617aac6de42bb602895c0b52/federation/pkg/kubefed/join.go#L277)):*
> ```Go
> 
> 	// Check for an existing service account with that name. Delete it if it exists.
> 	err := clusterClient.Core().ServiceAccounts(namespace).Delete(saName, &metav1.DeleteOptions{})
> ```

In line with the previous comment, I think a TODO explaining what is needed should be added here.
Options are:
1 - we generate and use an unique id (but we will need to remember it in subsequent calls, especially in unjoin, and if join is tried again)
2 - append this federation name, rather then "federation-controller-manager" that u have used to give some amount of uniqueness.
We will anyways need to brainstorm more on it.. :-)
As of now, probably only todo, suffices (or may be implement 2)

---

*[federation/pkg/kubefed/join.go, line 278 at r1](https://reviewable.kubernetes.io:443/reviews/kubernetes/kubernetes/42042#-KdlNJ52QtVtt5g-NJm8:-KdlNJ52QtVtt5g-NJm9:bfzpvl3) ([raw file](https://github.com/kubernetes/kubernetes/blob/e9ab6462d4b5844b617aac6de42bb602895c0b52/federation/pkg/kubefed/join.go#L278)):*
> IsNotFound
If the service account already existed, wouldn't it also mean that the secret created by it (by whoever created the service account) would also exist
We should delete that too, I guess!
Also, I am not entirely sure why would u want to delete service account, if it already exists, rather then just use if it already exists. 
Is it to invalidate the existing secret data ?

---

*[federation/pkg/kubefed/join.go, line 292 at r1](https://reviewable.kubernetes.io:443/reviews/kubernetes/kubernetes/42042#-KdlOt0hTE9b-BL7PaOM:-KdlOt0hTE9b-BL7PaON:b-7zmlwv) ([raw file](https://github.com/kubernetes/kubernetes/blob/e9ab6462d4b5844b617aac6de42bb602895c0b52/federation/pkg/kubefed/join.go#L292)):*
> ```Go
> 		}
> 
> 		return false, fmt.Errorf("service account still exists in cluster")
> ```

I am not sure, but I guess if u return error then the poll loop would exit with error.
Effectively exiting this loop after 1 try of get().
If you want to continue, u need to 
return false, nil
( but I am not sure about this one, and right now too lazy to verify :) )

---

*[federation/pkg/kubefed/join.go, line 325 at r1](https://reviewable.kubernetes.io:443/reviews/kubernetes/kubernetes/42042#-KdlSx4V2rtqkUBMkDJc:-KdlSx4V2rtqkUBMkDJd:bsl1fwl) ([raw file](https://github.com/kubernetes/kubernetes/blob/e9ab6462d4b5844b617aac6de42bb602895c0b52/federation/pkg/kubefed/join.go#L325)):*
> namespace
Doesnt this mean, that this role binding would allow access to objects only in the namespace pointed by "namespace", even if it actually binds to a cluster role with all privileges.
I am not very sure about this, but the documentation sort of says so -> https://kubernetes.io/docs/admin/authorization/

---

*[federation/pkg/kubefed/join.go, line 357 at r1](https://reviewable.kubernetes.io:443/reviews/kubernetes/kubernetes/42042#-KdlXR7Rz5mTFKdJp5Qp:-KdlXR7Rz5mTFKdJp5Qq:b-ws54l4) ([raw file](https://github.com/kubernetes/kubernetes/blob/e9ab6462d4b5844b617aac6de42bb602895c0b52/federation/pkg/kubefed/join.go#L357)):*
> ```Go
> 		sa, err = clusterClient.Core().ServiceAccounts(namespace).Get(saName, metav1.GetOptions{})
> 		if err != nil {
> 			return false, err
> ```

Similar to the previous comment about pollInfinite()
I think u need to use ->
return false, nil

---

*[federation/pkg/kubefed/join.go, line 380 at r1](https://reviewable.kubernetes.io:443/reviews/kubernetes/kubernetes/42042#-KdlYNOytjgn-qFuwAJ_:-KdlYNOytjgn-qFuwAJa:bebgl1a) ([raw file](https://github.com/kubernetes/kubernetes/blob/e9ab6462d4b5844b617aac6de42bb602895c0b52/federation/pkg/kubefed/join.go#L380)):*
> ```Go
> 	v1Secret := api.Secret{
> 		ObjectMeta: metav1.ObjectMeta{
> 			Name: secretName,
> ```

Wouldn't u need namespace here ?

---

*[federation/pkg/kubefed/unjoin.go, line 77 at r1](https://reviewable.kubernetes.io:443/reviews/kubernetes/kubernetes/42042#-KdlYdsikj00EGjq7v23:-KdlYdsikj00EGjq7v24:bidgjna) ([raw file](https://github.com/kubernetes/kubernetes/blob/e9ab6462d4b5844b617aac6de42bb602895c0b52/federation/pkg/kubefed/unjoin.go#L77)):*
> ```Go
> 
> // unjoinFederation is the implementation of the `unjoin` command.
> func (u *unjoinFederation) Run(f cmdutil.Factory, cmdOut, cmdErr io.Writer, config util.AdminConfig) error {
> ```

Wouldn't we need to remove whtever objects that we created in join when the cluster is removed from this federation.?

---

*[federation/pkg/kubefed/util/util.go, line 50 at r1](https://reviewable.kubernetes.io:443/reviews/kubernetes/kubernetes/42042#-KdlZQpqZ4ErwyjCE4nj:-KdlZQpqZ4ErwyjCE4nk:b-wqy6fd) ([raw file](https://github.com/kubernetes/kubernetes/blob/e9ab6462d4b5844b617aac6de42bb602895c0b52/federation/pkg/kubefed/util/util.go#L50)):*
> ```Go
> 	// accounts are used by the Federation control plane to access
> 	// the cluster's API server.
> 	clusterServiceAccountNamePrefix = "federation-controller-manager"
> ```

A comment about this name is given at usage. I guess for now its not a hard requirement to have an unique name, but it certainly needs a thought.

---


*Comments from [Reviewable](https://reviewable.kubernetes.io:443/reviews/kubernetes/kubernetes/42042)*
<!-- Sent from Reviewable.io -->

@perotinus
Copy link
Contributor Author

Thanks for the review! I have a few questions, so I've left a few

Given the code freeze coming up, I'm not sure how much of the TODO work I'll be able to get done and tested. I think supporting old clusters is probably pretty important, so I'm going to start on that now.


Review status: 0 of 5 files reviewed at latest revision, 26 unresolved discussions.


federation/pkg/federation-controller/util/cluster_util.go, line 69 at r1 (raw file):

Previously, irfanurrehman wrote…

you can as well pass the secret to this function, which u already have here.
It will avoid the getSecret() call twice.

Done, as per your comment below about removing GetterForCluster(). This is definitely cleaner. Thanks!


federation/pkg/federation-controller/util/cluster_util.go, line 72 at r1 (raw file):

Previously, irfanurrehman wrote…

you return nil, nil in case SecretRef.Name == nil from GetSecret() , which unnecessarily traces the rest of the code.

	if secretName == "" {
		return nil, nil
	}

You can as well format an error in return from GetSecret() or handle the scenario here itself, with this check not needed inside GetSecret()

I've reworked this, so this shouldn't be a problem anymore. Thanks!


federation/pkg/federation-controller/util/cluster_util.go, line 78 at r1 (raw file):

Previously, irfanurrehman wrote…

Assigning to clusterConfig without verifying error, clusterConfig could be null?

Yes, that's my bad. I went through and added error handling but missed this one.


federation/pkg/federation-controller/util/cluster_util.go, line 97 at r1 (raw file):

Previously, irfanurrehman wrote…

U can as well pass the secret object here, rather then the cluster object, because u already have it earlier.
Or even secret.Data, because that is what you consume in the getter().
Also, there wont be any need of nested functions GetterForCluster() and then GetterForSecret(), one function would suffice.

Done.


federation/pkg/federation-controller/util/cluster_util.go, line 100 at r1 (raw file):

Previously, irfanurrehman wrote…

you sort of have already done this check earlier (in BuildClusterConfig at line 65), and then call KubeconfigGetterForCluster(), so probably not needed here again.

Thanks! This code is gone now, so this is fixed.


federation/pkg/federation-controller/util/cluster_util.go, line 109 at r1 (raw file):

Previously, irfanurrehman wrote…

s/GetterForSecret/GetSecret
is this statement true or a copy paste mistake ->> "It is used to inject a different implementation in tests" ?

Yep. This was a copy-paste mistake. All fixed.


federation/pkg/federation-controller/util/cluster_util.go, line 110 at r1 (raw file):

Previously, irfanurrehman wrote…

You need not export the function if used only here.

Done.


federation/pkg/federation-controller/util/cluster_util.go, line 148 at r1 (raw file):

Previously, irfanurrehman wrote…

As in the comment on line 97, you probably don't need the nested function.

Done.


federation/pkg/kubefed/join.go, line 150 at r1 (raw file):

Previously, irfanurrehman wrote…

The info message ideally should be complete because we create somethings in target cluster and somethings in base cluster.
Creating federation system namespace in the joining cluster

Done.


federation/pkg/kubefed/join.go, line 151 at r1 (raw file):

Previously, irfanurrehman wrote…

You need to use the dryRun flag and ensure not to create object in the target cluster if dryRun is true.

Done.


federation/pkg/kubefed/join.go, line 153 at r1 (raw file):

Previously, irfanurrehman wrote…

Same as previous comment about the info message (and please apply it to all subsequent info messages)

Done.


federation/pkg/kubefed/join.go, line 158 at r1 (raw file):

Previously, irfanurrehman wrote…

Same comment for the use of dryRun, and please apply the same to all subsequent functions.

Done.


federation/pkg/kubefed/join.go, line 249 at r1 (raw file):

Previously, irfanurrehman wrote…

doesn't exist
doesn't already exist

Done.


federation/pkg/kubefed/join.go, line 259 at r1 (raw file):

Previously, irfanurrehman wrote…

Could not create federation-system namespace in client
Could not create federation-system namespace in joining cluster.

Hmm. I think I'd prefer to keep this the way it is; this function has no way of knowing that the passed-in client set is for a joining cluster, and I'd rather not make assumptions about the arguments in the logs. WDYT?


federation/pkg/kubefed/join.go, line 265 at r1 (raw file):

Previously, irfanurrehman wrote…

s/clusterClienr/clusterClient

Done.


federation/pkg/kubefed/join.go, line 268 at r1 (raw file):

Previously, irfanurrehman wrote…

Because the same cluster can join multiple federations (with a possibility that 2 admins might use a same context name for the cluster), I think we should contemplate getting a more unique name for the service account.

Hmm. That's a good point; I didn't know that clusters could join multiple federations.

What would be unique to those two federation clusters? My concern is that unjoin (when implemented) will need to be able to delete these deterministically, and with a random identifier, that will be not as easy.

I see your comment below. I've added a TODO about this.


federation/pkg/kubefed/join.go, line 277 at r1 (raw file):

Previously, irfanurrehman wrote…

In line with the previous comment, I think a TODO explaining what is needed should be added here.
Options are:
1 - we generate and use an unique id (but we will need to remember it in subsequent calls, especially in unjoin, and if join is tried again)
2 - append this federation name, rather then "federation-controller-manager" that u have used to give some amount of uniqueness.
We will anyways need to brainstorm more on it.. :-)
As of now, probably only todo, suffices (or may be implement 2)

Done. #2 was easy to implement, and is certainly more unique, so I did it.


federation/pkg/kubefed/join.go, line 278 at r1 (raw file):

Previously, irfanurrehman wrote…

IsNotFound
If the service account already existed, wouldn't it also mean that the secret created by it (by whoever created the service account) would also exist
We should delete that too, I guess!
Also, I am not entirely sure why would u want to delete service account, if it already exists, rather then just use if it already exists.
Is it to invalidate the existing secret data ?

I suppose we could use the existing service account. I preferred to recreate it anew and not use the existing secret data, but I'm not strongly attached to this idea. Does anyone see an issue with reusing the service account and its credentials for the new cluster?

Actually, this might be a hacky workaround to the "cluster in multiple federations" issue: two federations with the same name will have the same service account, and can reuse credentials. This seems not great, but it would work.

The secret in the joining cluster would be deleted when the SA is deleted, I believe, though I haven't verified this.


federation/pkg/kubefed/join.go, line 292 at r1 (raw file):

Previously, irfanurrehman wrote…

I am not sure, but I guess if u return error then the poll loop would exit with error.
Effectively exiting this loop after 1 try of get().
If you want to continue, u need to
return false, nil
( but I am not sure about this one, and right now too lazy to verify :) )

Agreed. Leaving this for now, until we decide about using the existing service account.


federation/pkg/kubefed/join.go, line 325 at r1 (raw file):

Previously, irfanurrehman wrote…

namespace
Doesnt this mean, that this role binding would allow access to objects only in the namespace pointed by "namespace", even if it actually binds to a cluster role with all privileges.
I am not very sure about this, but the documentation sort of says so -> https://kubernetes.io/docs/admin/authorization/

Not exactly. This is a ClusterRoleBinding, which lives outside of namespaces and allows access to all resources. I originally tried using regular RoleBindings, but even with all roles allowed they didn't have access to resources in other namespaces.

Here, I believe that the namespace argument says "bind the SA named saName in namespace", rather than affecting the ClusterBinding's namepsace.


federation/pkg/kubefed/join.go, line 357 at r1 (raw file):

Previously, irfanurrehman wrote…

Similar to the previous comment about pollInfinite()
I think u need to use ->
return false, nil

You are correct. Thanks!


federation/pkg/kubefed/join.go, line 380 at r1 (raw file):

Previously, irfanurrehman wrote…

Wouldn't u need namespace here ?

That's a good point. THanks!


federation/pkg/kubefed/unjoin.go, line 77 at r1 (raw file):

Previously, irfanurrehman wrote…

Wouldn't we need to remove whtever objects that we created in join when the cluster is removed from this federation.?

Absolutely! I had deferred this until a pass on the rest of the code. I might do it in a follow-up PR, since there is a manual workaround ATM (ie, lots of kubectl deletes)


federation/pkg/kubefed/util/util.go, line 50 at r1 (raw file):

Previously, irfanurrehman wrote…

A comment about this name is given at usage. I guess for now its not a hard requirement to have an unique name, but it certainly needs a thought.

I've removed this, in favor of a bit more uniqueness.


federation/cluster/federation-up.sh, line 98 at r1 (raw file):

Previously, irfanurrehman wrote…

Any specific reason for this change?
I think the intention of --v=2 is different (for example intended for additional information from the tool or the libs the tool is using)
When federation-up.sh prints such info, it might surprise users (for example, an info I get from kubefed unjoin at this verbosity is details about all flags passed to kubefed join, which the federation-up.sh user is unaware of).
Other point of view might be why do this only for join, why not for init, or unjoin.. ?

Well, there was: I forgot to push my latest changes after I removed the debugging code. :) Sorry about that!


Comments from Reviewable

@irfanurrehman
Copy link
Contributor

@perotinus, please update me once you want me to have a look again!

@perotinus
Copy link
Contributor Author

@irfanurrehman I think you can take a look now; I've replied to all of your previous comments, and I think that @madhusudancs (I believe; correct me if I've remembered wrong, Madhu) suggested that I do the rest of the TODO work in follow-up PRs.

Madhu also suggested that I rebase this PR off of your PR that checks for the RBAC version on the API server. I took a look and I'm not sure it'll fully solve my problem here: it looks like you assume that the API server will support some version of RBAC, where I need to check whether the server supports RBAC at all and use the old behavior if it does not. I can use your code when I make the RBAC calls, but will need a parallel check for the existence of the RBAC group at all.

Thanks!

@irfanurrehman
Copy link
Contributor

@perotinus, I think the reason @madhusudancs suggested you to rebase on top of mine is because, u would need to solve the conflicts anyways (there are changes in common part of the code), when it comes to merging.. :-).
About, verifying the RBAC versions, yes what you are saying is right, (you can use the code in my PR for reference, however). You would need to do additional magic of your own...

@irfanurrehman
Copy link
Contributor

Review status: 0 of 5 files reviewed at latest revision, 26 unresolved discussions.


federation/pkg/federation-controller/util/cluster_util.go, line 72 at r1 (raw file):

Previously, perotinus (Jonathan MacMillan) wrote…

I've reworked this, so this shouldn't be a problem anymore. Thanks!

@perotinus, have you pushed ur changes.
I find the same problem still there in code, infact did go to verify on ur branch also, but still find the old code.
https://github.com/perotinus/kubernetes/blob/svcaccounts/federation/pkg/federation-controller/util/cluster_util.go#L112
??


federation/pkg/federation-controller/util/cluster_util.go, line 78 at r1 (raw file):

Previously, perotinus (Jonathan MacMillan) wrote…

Yes, that's my bad. I went through and added error handling but missed this one.

same here,
https://github.com/perotinus/kubernetes/blob/svcaccounts/federation/pkg/federation-controller/util/cluster_util.go#L79
??


Comments from Reviewable

@k8s-github-robot k8s-github-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Feb 27, 2017
@k8s-github-robot k8s-github-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Feb 27, 2017
@perotinus perotinus force-pushed the svcaccounts branch 2 times, most recently from 403f745 to 68f560b Compare February 27, 2017 23:30
@perotinus
Copy link
Contributor Author

@irfanurrehman OK, this should be updated now. I've rebased as well. PTAL when you get a chance.

Not sure what happened this weekend. Sorry about that!

@irfanurrehman
Copy link
Contributor

irfanurrehman commented Feb 28, 2017

@perotinus, please go ahead and implement the unit test also for this.
Please let me know once you are done; I will verify it whole then.

@perotinus perotinus force-pushed the svcaccounts branch 2 times, most recently from 3783b25 to 98f119b Compare March 1, 2017 00:05
@perotinus perotinus force-pushed the svcaccounts branch 2 times, most recently from b79484b to d02e056 Compare May 16, 2017 21:16
@perotinus
Copy link
Contributor Author

@k8s-bot test this

@k8s-github-robot k8s-github-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label May 17, 2017
@k8s-github-robot k8s-github-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label May 17, 2017
@madhusudancs
Copy link
Contributor

Awesome! Thanks for digging through these issues and fixing them.

Requires a rebase and I have a few minor comments. Mostly nits. Let me know once you address them. I will apply the labels.


Reviewed 4 of 7 files at r6, 4 of 7 files at r7.
Review status: 8 of 11 files reviewed at latest revision, 14 unresolved discussions.


federation/pkg/federation-controller/util/cluster_util.go, line 81 at r4 (raw file):

Previously, perotinus (Jonathan MacMillan) wrote…

Hmm. Conceptually what I want to get across is most briefly expressed this way.

Would (tokenFound && !caFound) || (!tokenFound && caFound) be clearer?

Yeah! Although it's verbose, it is very clear that if you have one or the other then execute the if block.


federation/pkg/kubefed/join.go, line 160 at r4 (raw file):

	saName := util.ClusterServiceAccountName(j.commonOptions.Name, j.commonOptions.Host)
	sa, err := rbacVersionedClientset.Core().ServiceAccounts(j.commonOptions.FederationSystemNamespace).Get(saName, metav1.GetOptions{})
	if sa != nil {

Why remove this?


federation/pkg/kubefed/join.go, line 568 at r4 (raw file):

	_, err := clusterClientset.Core().ServiceAccounts(namespace).Create(sa)
	if err != nil {
		glog.V(2).Infof("Could not create service account in joining cluster: %v", err)

Not necessary anymore?


federation/pkg/kubefed/join.go, line 502 at r7 (raw file):

		return configMap
	}
	configMap.Data[util.KubeDnsStubDomains] = fmt.Sprintf(`{"%s":["%s"]}`, dnsZoneName, nameServer)

I am somewhat confused about this. May be rebase?


federation/pkg/kubefed/join_test.go, line 445 at r7 (raw file):

	}

	saName := saName(clusterName)

Can we call this variable something else? It doesn't matter that much, this is a test code and it is only used here, but it is worth differentiating the function name and the variable name. But I don't feel too strongly about it. So feel free to ignore as well.


federation/pkg/kubefed/join_test.go, line 597 at r7 (raw file):

		saName := saName(clusterName)
		annotations := map[string]string{
			kubectl.ServiceAccountNameAnnotation: saName,

Just directly call the function here instead of trying to rename the variable here. I think that's Ok because it's test code.


federation/pkg/kubefed/unjoin.go, line 322 at r7 (raw file):

	}

	if err := unjoiningClusterClientset.Rbac().ClusterRoleBindings().Delete(clusterRoleName, &metav1.DeleteOptions{}); err != nil && !errors.IsMethodNotSupported(err) && !errors.IsNotFound(err) {

I really dislike the if err := someLongStatement(with many args); err != nil {pattern even though it is a common pattern that we use everywhere in our codebase. It hides the real condition that's being checked. In this case, the condition also has a bunch of components which makes things even more difficult to read on the same line. So could you please split this into:

err := unjoiningClusterClientset.Rbac().ClusterRoleBindings().Delete(clusterRoleName, &metav1.DeleteOptions{})
if err != nil && !errors.IsMethodNotSupported(err) && !errors.IsNotFound(err) {
    ...
}

?


federation/pkg/kubefed/unjoin.go, line 325 at r7 (raw file):

		return err
	}
	if err := unjoiningClusterClientset.Rbac().ClusterRoles().Delete(clusterRoleName, &metav1.DeleteOptions{}); err != nil && !errors.IsMethodNotSupported(err) && !errors.IsNotFound(err) {

Same here.


Comments from Reviewable

@perotinus
Copy link
Contributor Author

Review status: 6 of 11 files reviewed at latest revision, 14 unresolved discussions.


federation/pkg/kubefed/join.go, line 160 at r4 (raw file):

Previously, madhusudancs (Madhusudan.C.S) wrote…

Why remove this?

Oh, I remember now: sa was always non-nil, it was just empty if there was no service account in the cluster, so this check wasn't doing anything. I assumed this was due to a change elsewhere in Kubernetes.

I suppose I could re-add this with a more complicated check that the SA is non-empty.


federation/pkg/kubefed/join.go, line 568 at r4 (raw file):

Previously, madhusudancs (Madhusudan.C.S) wrote…

Not necessary anymore?

I think I removed this because the caller was also logging.


federation/pkg/kubefed/join.go, line 502 at r7 (raw file):

Previously, madhusudancs (Madhusudan.C.S) wrote…

I am somewhat confused about this. May be rebase?

Done.


federation/pkg/kubefed/join_test.go, line 445 at r7 (raw file):

Previously, madhusudancs (Madhusudan.C.S) wrote…

Can we call this variable something else? It doesn't matter that much, this is a test code and it is only used here, but it is worth differentiating the function name and the variable name. But I don't feel too strongly about it. So feel free to ignore as well.

Done.


federation/pkg/kubefed/join_test.go, line 597 at r7 (raw file):

Previously, madhusudancs (Madhusudan.C.S) wrote…

Just directly call the function here instead of trying to rename the variable here. I think that's Ok because it's test code.

Hmm. I use saName just below as well, so I think I have to create the local variable.


federation/pkg/kubefed/unjoin.go, line 322 at r7 (raw file):

Previously, madhusudancs (Madhusudan.C.S) wrote…

I really dislike the if err := someLongStatement(with many args); err != nil {pattern even though it is a common pattern that we use everywhere in our codebase. It hides the real condition that's being checked. In this case, the condition also has a bunch of components which makes things even more difficult to read on the same line. So could you please split this into:

err := unjoiningClusterClientset.Rbac().ClusterRoleBindings().Delete(clusterRoleName, &metav1.DeleteOptions{})
if err != nil && !errors.IsMethodNotSupported(err) && !errors.IsNotFound(err) {
    ...
}

?

Done.


federation/pkg/kubefed/unjoin.go, line 325 at r7 (raw file):

Previously, madhusudancs (Madhusudan.C.S) wrote…

Same here.

Done.


Comments from Reviewable

@perotinus
Copy link
Contributor Author

perotinus commented May 19, 2017

@madhusudancs OK, this should be ready for re-review now.

@madhusudancs
Copy link
Contributor

Reviewed 3 of 7 files at r7, 2 of 2 files at r8.
Review status: 10 of 11 files reviewed at latest revision, 9 unresolved discussions.


federation/pkg/kubefed/join.go, line 160 at r4 (raw file):

Previously, perotinus (Jonathan MacMillan) wrote…

Oh, I remember now: sa was always non-nil, it was just empty if there was no service account in the cluster, so this check wasn't doing anything. I assumed this was due to a change elsewhere in Kubernetes.

I suppose I could re-add this with a more complicated check that the SA is non-empty.

The problem is, this code is now not achieving what we want, i.e. making sure the object doesn't exist.

I would suggest restructuring this as:

// Make sure there is no existing service account in the joining cluster.
saName := util.ClusterServiceAccountName(j.commonOptions.Name, j.commonOptions.Host)
sa, err = rbacVersionedClientset.Core().ServiceAccounts(j.commonOptions.FederationSystemNamespace).Get(saName, metav1.GetOptions{})
if errors.IsNotFound(err) {
    return nil
} else if err != nil {
    return err
} else if sa != nil {
    return fmt.Errorf("service account already exists in joining cluster")
}

My suspicion is somewhere in the Get() call chain somebody is doing a returning a

if err != nil {
    return &v1.ServiceAccount{}, err
}

whereas the convention is to

if err != nil {
    return nil, err
}

This explains why you are getting an empty object. So checking for the errors first and then the service account object helps here. Makes sense?


federation/pkg/kubefed/join.go, line 568 at r4 (raw file):

Previously, perotinus (Jonathan MacMillan) wrote…

I think I removed this because the caller was also logging.

Ok.


Comments from Reviewable

@perotinus
Copy link
Contributor Author

Review status: 10 of 11 files reviewed at latest revision, 8 unresolved discussions, some commit checks failed.


federation/pkg/kubefed/join.go, line 160 at r4 (raw file):

Previously, madhusudancs (Madhusudan.C.S) wrote…

The problem is, this code is now not achieving what we want, i.e. making sure the object doesn't exist.

I would suggest restructuring this as:

// Make sure there is no existing service account in the joining cluster.
saName := util.ClusterServiceAccountName(j.commonOptions.Name, j.commonOptions.Host)
sa, err = rbacVersionedClientset.Core().ServiceAccounts(j.commonOptions.FederationSystemNamespace).Get(saName, metav1.GetOptions{})
if errors.IsNotFound(err) {
    return nil
} else if err != nil {
    return err
} else if sa != nil {
    return fmt.Errorf("service account already exists in joining cluster")
}

My suspicion is somewhere in the Get() call chain somebody is doing a returning a

if err != nil {
    return &v1.ServiceAccount{}, err
}

whereas the convention is to

if err != nil {
    return nil, err
}

This explains why you are getting an empty object. So checking for the errors first and then the service account object helps here. Makes sense?

Done.


Comments from Reviewable

@madhusudancs
Copy link
Contributor

@perotinus thanks for fighting through this! And finally! Everything LGTM, but let's wait for federation presubmits situation to settle down before adding the labels.


Reviewed 1 of 1 files at r9, 1 of 1 files at r10.
Review status: all files reviewed at latest revision, 7 unresolved discussions, some commit checks failed.


Comments from Reviewable

@perotinus
Copy link
Contributor Author

@k8s-bot pull-kubernetes-federation-e2e-gce test this

@madhusudancs
Copy link
Contributor

/lgtm

Finally!

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label May 24, 2017
@janetkuo
Copy link
Member

/approve

@k8s-github-robot
Copy link

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: janetkuo, madhusudancs, perotinus

Needs approval from an approver in each of these OWNERS Files:

You can indicate your approval by writing /approve in a comment
You can cancel your approval by writing /approve cancel in a comment

@k8s-github-robot k8s-github-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label May 24, 2017
@k8s-github-robot
Copy link

@k8s-bot test this [submit-queue is verifying that this PR is safe to merge]

@k8s-ci-robot
Copy link
Contributor

k8s-ci-robot commented May 24, 2017

@perotinus: The following test(s) failed:

Test name Commit Details Rerun command
pull-kubernetes-unit af2a8f7 link @k8s-bot pull-kubernetes-unit test this
pull-kubernetes-e2e-kops-aws af2a8f7 link @k8s-bot pull-kubernetes-e2e-kops-aws test this

Full PR test history. Your PR dashboard. Please help us cut down on flakes by linking to an open issue when you hit one in your PR.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed here.

@k8s-github-robot
Copy link

Automatic merge from submit-queue (batch tested with PRs 42042, 46139, 46126, 46258, 46312)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. lgtm "Looks good to me", indicates that a PR is ready to be merged. release-note Denotes a PR that will be considered when it comes time to generate release notes. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.