-
Notifications
You must be signed in to change notification settings - Fork 13
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 support for adopting external cluster #306
base: main
Are you sure you want to change the base?
Conversation
Signed-off-by: Paolo Dettori <[email protected]>
cp := common.GenerateControlPlane(c.Name, string(controlPlaneType), "", hook, hookVars) | ||
|
||
util.PrintStatus(fmt.Sprintf("Adopting control plane %s of type %s ...", c.Name, controlPlaneType), done, &wg, chattyStatus) | ||
if err := cl.Create(context.TODO(), cp, &client.CreateOptions{}); err != 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.
FYI, all writes should supply a FieldManager in the XXXOptions.
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 prefer to leave this to a separate campaign/PR as there are many other places where there are writes so should probably be done consistently everywhere.
Yes, WIP. This still needs a doc update. I do not understand how the user passes the kubeconfig of the cluster to be adopted into the adopt subcommand. The adopt subcommand needs to get added into the command tree. |
@MikeSpreitzer - correct on all points - I started with the easiest updates, now working on the "adopt" subcommand and getting the kubeconfig. The current idea is that the user will supply the name of the context (and optionally the path of the Kubeconfig) in the Kubeconfig for the cluster to adopt as flags of the adopt subcommand, and the such subcommand will take care of creating ClusterRoles, ClusterRoleBindings, SA and Token in the adopted cluster so that the Token + cluster endpoint + CA cert can be used to build a new Kubeconfig for the adopted cluster to store in a secret in the namespace associated with the control plane in the hosting cluster. |
Signed-off-by: Paolo Dettori <[email protected]>
Signed-off-by: Paolo Dettori <[email protected]>
Signed-off-by: Paolo Dettori <[email protected]>
@pdettori I think you have some changes in chart/templates/operator.yaml that may be accidental |
To create a control plane of type `external` with the required options, run the command: | ||
|
||
```shell | ||
kflex adopt --adopted-context <kubeconfig-context-of-external-cluster> cp5 |
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.
Could a separated kubeconfig be passed with KUBECONFIG=... kflex...
or `kflex --kubeconfig '?
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 no value is passed to --adopted-context
could it default to the current one... especially if one passes a separate kubeconfig with only one context in it?
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.
As this command both create the secret and create the CP... it would be hard to use in combination with KS future Helm chart. The process would be
- create/push the secret in the cluster manually or via kflex.
- tell KS helm to create a CP using it (either upon installation or later on)
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.
Could a separated kubeconfig be passed with
KUBECONFIG=... kflex...
or `kflex --kubeconfig '?
Yes. These are all the options for the adopt subcommand. You'd be looking at the --adopted-kubeconfig
flag.
$ kflex adopt --help
Adopt a control plane from an external cluster and switches the Kubeconfig context to
the current instance
Usage:
kflex adopt <name> [flags]
Flags:
-c, --adopted-context string path to adopted cluster context in adopted kubeconfig
-a, --adopted-kubeconfig string path to adopted cluster kubeconfig file. If unspecified, it uses the default Kubeconfig
-s, --chatty-status chatty status indicator (default true)
-x, --expiration-seconds int adopted token expiration in seconds. Default is one year. (default 31536000)
-h, --help help for adopt
-k, --kubeconfig string path to kubeconfig file
-p, --postcreate-hook string name of post create hook to run
-e, --set stringArray set post create hook variables, in the form name=value
-d, --skip-url-override skip URL override, used for local debugging only
-u, --url-override https://127.0.0.1 URL overrride for adopted cluster. Required when cluster address uses local host address, e.g. https://127.0.0.1
-v, --verbosity int log level
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 no value is passed to --adopted-context could it default to the current one... especially if one passes a separate kubeconfig with only one context in it?
Currently you do need to pass the context. It might be confusing in the common developer scenario where there is one kubeconfig with a context for each cluster, since the "current" context is typically the kubeflex hosting cluster context. It might be possible when a different Kubeconfig is used (which needs to be specified with --adopted-kubeconfig
) to check if there is only one context and in that case not require the use of --adopted-context
. But I would leave this as a future feature/PR if we really think it is useful.
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.
As this command both create the secret and create the CP... it would be hard to use in combination with KS future Helm chart...
One possible approach for using kflex to create bootstrap secret and the helm chart to create the CP would be to add an option to the adopt
subcommand --create-bootstrap-secret-only
- would that help?
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.
As this command both create the secret and create the CP... it would be hard to use in combination with KS future Helm chart...
One possible approach for using kflex to create bootstrap secret and the helm chart to create the CP would be to add an option to the
adopt
subcommand--create-bootstrap-secret-only
- would that help?
Assuming that one has a kubeconfig for the external cluster, I guess one can use kubectl to do it... that's way I was asking for clarifications on what the secret contains
kubectl create secret generic <cluster1-bootstrap-secret> --from-file=<cluster1-key>=<cluster1-kubeconfig-path> --namespace <cluster1-bootstrap-namespace>
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.
Since it is assumed that the bootstrap kubeconfig has only one context, you probably need first to extract a new kubeconfig only for the context of the adopted cluster, so you'd need to run first a command such as:
kubectl --kubeconfig=$SOURCE_KUBECONFIG config view --minify --flatten \
--context=$DESIRED_CONTEXT > $OUTPUT_KUBECONFIG
and then you can run the kubectl create secret generic ...
command
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.
Since a kubeconfig has a "current context", the natural default for --adopted-context
is the current context of the adopted kubeconfig.
BootstrapSecretRef contains a reference to the kubeconfig used to bootstrap adoption of | ||
an external cluster | ||
properties: | ||
inClusterKey: |
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.
Can you please clarify what is inClusterKey
?
Perhaps I missed, but I did not see a documentation of what the bootstrap secret should look like. This is useful if somebody wants/needs to create it without using kflex.
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 may see the defaults being used for the bootstrap secret in cmd/kflex/adopt/adopt.go
. The name is defined in:
func GenerateBoostrapSecretName(cpName string) string {
return fmt.Sprintf("%s-bootstrap", cpName)
}
namespace is SystemNamespace = "kubeflex-system"
and key is KubeconfigSecretKeyInCluster = "kubeconfig-incluster"
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.
To further clarify, inClusterKey
is the name of the secret generated by kflex from the bootstrap secret... right?
Since you have a default, can you make it not required?
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.
To further clarify, inClusterKey is the name of the secret generated by kflex from the bootstrap secret
No, inClusterKey
is the name of the key used for the kubeconfig, which is kubeconfig-incluster
. The name of the secret would be <cp-name>-bootstrap
. You do not have to provide these values in the ControlPlane CR, they are optional.
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.
(A) Since the golang source re-uses the existing type SecretReference
, regularity is implied here. It is a reference to the same sort of thing. Same schema for contents. (And, BTW, the expectation(s) on Secret contents should be documented in the comment on the SecretReference
type.) I am not sure yet whether this usage really is regular.
(B) @pdettori, what do you mean by "You do not have to provide these values in the ControlPlane CR, they are optional"? Which values are optional?
BootstrapSecretRef *SecretReference `json:"bootstrapSecretRef,omitempty"` | ||
// expiration time for token of adopted cluster | ||
// +optional | ||
AdoptedTokenExpirationSeconds *int64 `json:"adoptedTokenExpirationSeconds,omitempty"` |
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.
(A) If the type is "external", shouldn't this field's name start with "External" rather than "Adopted"?
(B) Is this parameter really only relevant to the case of external type? Isn't this a question that arises for every type (possibly excepting host)?
@@ -591,7 +591,7 @@ spec: | |||
- --secure-listen-address=0.0.0.0:8443 | |||
- --upstream=http://127.0.0.1:8080/ | |||
- --logtostderr=true | |||
- --v={{.Values.verbosity}} | |||
- --v=0 |
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 this better than simply omitting the -v
?
@@ -280,6 +280,8 @@ clusters registration and support for the [`ManifestWork` API](https://open-clus | |||
- vcluster: this is based on the [vcluster project](https://www.vcluster.com) and provides the ability to create pods in the hosting namespace of the hosting cluster. | |||
- host: this control plane type exposes the underlying hosting cluster with the same control plane abstraction | |||
used by the other control plane types. | |||
- external: this control plane type exposes an external cluster with the same control plane abstraction |
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 suspect that the intended significance of "external" may not be clear to readers who do not already know what we are trying to say. I would have words here explaining that this type of ControlPlane is a representation of a preexisting cluster, one that was not created by KubeFlex. And not the hosting cluster.
In this section, we will show an example of creating an external control plane to integrate | ||
a kind cluster named `ext1` into the same Docker network as your Kubeflex hosting cluster. This setup | ||
requires that both clusters are accessible from each other within the shared Docker network. |
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 find this confusing. It says that the purpose is "to integrate a kind cluster named ext1
into the same Docker network as your Kubeflex hosting cluster" BUT it also says that they already have to be on the same Docker network. So the integration is already done?
I expected that the main issue here is the creation of a ControlPlane that represents a preexisting cluster.
docker network inspect kind | jq '.[].Containers | to_entries[] | .value.Name' | ||
``` | ||
|
||
The output will list the container names for your clusters. For example: |
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.
"For example:" makes me expect that what's next is an example of the output from the above command. But that is not actually what comes next.
docker network ls | ||
``` | ||
|
||
Inspect any specific network to confirm its configuration using docker network inspect <network_name>. |
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.
Isn't there a simpler way? Looking at the output of docker inspect cluster1-control-plane
, for example, I see the following in the NetworkSettings.Networks.kind section (and "kind" is the only entry in NetworkSettings.Networks):
"DNSNames": [
"cluster1-control-plane",
"2e9a7ee1c5fe"
]
|
||
In this section, we will show an example of creating an external control plane to integrate | ||
a kind cluster named `ext1` into the same Docker network as your Kubeflex hosting cluster. This setup | ||
requires that both clusters are accessible from each other within the shared Docker network. |
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.
Isn't it true that Docker supports multiple networks, so "the" shared network is not a general statement? Isn't it true that the real constraint here is that the "external" cluster and the KubeFlex hosting cluster are both on the same Docker network, regardless of which one that is?
To set up the external cluster ext1 as a control plane named cpe, use the following command: | ||
|
||
```shell | ||
$ kflex adopt -c kind-ext1 -u https://ext1-control-plane:6443 |
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.
The meanings of "c" in -c
and "u" in -u
are not really obvious and I am never going to remember them. Since this is establishing a pattern for readers to follow, I would use the long flag names here.
|
||
### External clusters on a different host | ||
|
||
If the external cluster operates on a different host and your kubeconfig context |
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 "operates on a different host" really a critical precondition for not needing to specify a URL override?
@@ -314,6 +316,12 @@ To create a control plane of type `host` run the command: | |||
kflex create cp4 --type host | |||
``` | |||
|
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.
Users of the API are going to need an adequate explanation too. The documentation below for CLI users omits all mention of the bootstrap secret.
@@ -314,6 +316,12 @@ To create a control plane of type `host` run the command: | |||
kflex create cp4 --type host | |||
``` | |||
|
|||
To create a control plane of type `external` with the required options, run the command: |
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 command will also create a Secret object that lingers forever, right? If so then that seems like something worth mentioning. And if the Secret is eventually automatically deleted, users should be told about its temporary existence.
) | ||
|
||
func (r *ExternalReconciler) ReconcileKubeconfigFromBoostrapSecret(ctx context.Context, hcp *tenancyv1alpha1.ControlPlane) error { | ||
_ = clog.FromContext(ctx) |
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 does this statement accomplish?
} | ||
|
||
func deleteBoostrapSecret(crClient client.Client, ctx context.Context, hcp *tenancyv1alpha1.ControlPlane) error { | ||
_ = clog.FromContext(ctx) |
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 does this statement, and all the others like it, accomplish?
return deleteBoostrapSecret(r.Client, ctx, hcp) | ||
} | ||
|
||
func getKubeconfigFromBoostrapSecret(crClient client.Client, ctx context.Context, hcp *tenancyv1alpha1.ControlPlane) (*api.Config, 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.
Typo in the name here, missed the "t" before "strap"?
return r.Client.Update(ctx, kubeConfigSecret) | ||
} | ||
|
||
func deleteBoostrapSecret(crClient client.Client, ctx context.Context, hcp *tenancyv1alpha1.ControlPlane) 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.
Typo in this name, missed the "t" at the end of "Boot"?
return nil, err | ||
} | ||
|
||
key := util.DefaultString(hcp.Spec.BootstrapSecretRef.Key, util.KubeconfigSecretKeyInCluster) |
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.
Isn't the external kubeconfig the one commonly used? The whole point of adopting an external cluster is that it is external to the KubeConfig hosting cluster.
return fmt.Errorf("error creating ClusterRoleBinding on the adopted cluster: %v", err) | ||
} | ||
|
||
if err = reconcileAdoptedServiceAccount(aClientset.CoreV1().ServiceAccounts(adoptedClusterSANamespace), adoptedClusterSAName); err != 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.
Shouldn't ctx
be passed along, in this and following calls?
|
||
kubeConfig := api.NewConfig() | ||
|
||
kubeConfig.Clusters[context.Cluster] = &api.Cluster{ |
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.
Aren't there additional fields of cluster
that should be copied? Is there a reason to not copy cluster
as a whole rather than a few enumerated fields?
Token: token, | ||
} | ||
|
||
kubeConfig.Contexts[hcp.Name] = &api.Context{ |
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.
Why not copy the default namespace and the extensions?
Namespace: namespace, | ||
}, | ||
Type: corev1.SecretTypeOpaque, | ||
Data: map[string][]byte{util.KubeconfigSecretKeyInCluster: kubeconfig}, |
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 util.KubeconfigSecretKeyInCluster
the right key (and the only right key)? Isn't this kubeconfig usable from anywhere?
@@ -137,7 +137,8 @@ func IsAPIServerDeploymentReady(log logr.Logger, c client.Client, hcp tenancyv1a | |||
switch hcp.Spec.Type { | |||
|
|||
case tenancyv1alpha1.ControlPlaneTypeHost: | |||
// host is always available | |||
case tenancyv1alpha1.ControlPlaneTypeExternal: | |||
// host or external is always available |
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 line is only reached when hcp.Spec.Type
is tenancyv1alpha1.ControlPlaneTypeExternal
. When hcp.Spec.Type
is tenancyv1alpha1.ControlPlaneTypeHost
), the previous clause is reached --- and now does nothing (whereas it formerly did a return true, nil
). I suspect that you meant instead of two clauses to have one clause with two matching expressions.
cx.Context(chattyStatus, false, false, false) | ||
|
||
controlPlaneType := tenancyv1alpha1.ControlPlaneTypeExternal | ||
util.PrintStatus(fmt.Sprintf("Adopting control plane %s of type %s ...", c.Name, controlPlaneType), done, &wg, chattyStatus) |
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.
Did you know that a Cobra Command
has methods like Context
, ErrOrStderr
, OutOrStdout
?
controlPlaneType := tenancyv1alpha1.ControlPlaneTypeExternal | ||
util.PrintStatus(fmt.Sprintf("Adopting control plane %s of type %s ...", c.Name, controlPlaneType), done, &wg, chattyStatus) | ||
|
||
adoptedKubeconfig := getAdoptedKubeconfig(c) |
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.
Since there are two kubeconfigs for the adopted cluster (one used for its bootstrap Secret, one used for the long-lived Secret), I do not find the names here clear. Would "bootstrap" be better than "adopted" here?
@@ -201,6 +231,17 @@ func init() { | |||
createCmd.Flags().BoolVarP(&chattyStatus, "chatty-status", "s", true, "chatty status indicator") | |||
createCmd.Flags().StringArrayVarP(&hookVars, "set", "e", []string{}, "set post create hook variables, in the form name=value ") | |||
|
|||
adoptCmd.Flags().StringVarP(&kubeconfig, "kubeconfig", "k", "", "path to kubeconfig file") |
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.
The reader should be told which kubeconfig. There are three involved in adoption: one for the KubeFlex hosting cluster, one to read/write the bootstrap Secret, and one constructed for users of the ControlPlane.
AdoptedKubeconfig: adoptedKubeconfig, | ||
AdoptedContext: adoptedContext, | ||
AdoptedURLOverride: adoptedURLOverride, | ||
AdoptedTokenExpirationSeconds: adoptedTokenExpirationSeconds, | ||
SkipURLOverride: skipURLOverride, |
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.
FYI, you wouldn't have to do this copying if the subcommand options were in a struct that contained the cobra.Command
rather than (roughly) the other way around. The OCM status addon also shows two more patterns that lack such copying.
return getKubeConfigFromEnv(c.Kubeconfig) | ||
} | ||
|
||
func getKubeConfigFromEnv(kubeconfig string) 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.
Why does this func take a kubeconfig string
argument? The one and only call site guarantees that the string is empty.
fmt.Fprintf(os.Stderr, "Error getting kubeflex client: %v\n", err) | ||
os.Exit(1) | ||
} | ||
cl := *clp |
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.
Why does kfclient.GetClient
return a pointer? Every call site does nothing with that pointer but dereference it --- to get an interface value that could itself be nil.
@@ -201,6 +231,17 @@ func init() { | |||
createCmd.Flags().BoolVarP(&chattyStatus, "chatty-status", "s", true, "chatty status indicator") | |||
createCmd.Flags().StringArrayVarP(&hookVars, "set", "e", []string{}, "set post create hook variables, in the form name=value ") | |||
|
|||
adoptCmd.Flags().StringVarP(&kubeconfig, "kubeconfig", "k", "", "path to kubeconfig file") |
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 there a default to the KUBECONFIG environment variable with standard semantics? Readers should probably be told the answer. And the answer should probably be "yes". FYI, in https://github.com/kubestellar/kubestellar/blob/v0.14.2/pkg/client-options/options.go we had the standard semantics and common flags wrapped up in a neat little bundle.
endpoint := adoptedURLOverride | ||
if endpoint == "" { | ||
endpoint = cluster.Server | ||
if !isValidServerURL(endpoint) { |
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.
Why is this validity not checked for the case where adoptedURLOverride
is not the empty string?
|
||
If the external cluster operates on a different host and your kubeconfig context | ||
contains an endpoint for that cluster which is not using a loopback interface, | ||
specifying an override URL is unnecessary. |
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.
In the current draft, I think the reader at this point is surprised by talk about an override URL. The reader has not seen this term before and does not know what it is (much less that the -u
above was doing that).
adoptCmd.Flags().IntVarP(&verbosity, "verbosity", "v", 0, "log level") // TODO - figure out how to inject verbosity | ||
adoptCmd.Flags().StringVarP(&Hook, "postcreate-hook", "p", "", "name of post create hook to run") | ||
adoptCmd.Flags().BoolVarP(&chattyStatus, "chatty-status", "s", true, "chatty status indicator") | ||
adoptCmd.Flags().BoolVarP(&skipURLOverride, "skip-url-override", "d", false, "skip URL override, used for local debugging only") |
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.
Why is there a --skip-url-override
option? Is there any reason for this bit to be different from override == ""
?
// check if the current server URL in the adopted cluster kubeconfig is using | ||
// a local address, which would not work in a container | ||
func isValidServerURL(serverURL string) bool { | ||
localAddresses := []string{"127.0.0.1", "localhost", "::1"} |
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.
strings.Contains("2600:0062::1", "::1")
is true.
strings.Contains("nonlocalhost", "localhost")
is true.
strings.Contains("127.0.0.100", "127.0.0.1")
is true (and strings.Contains("127.0.0.2", "127.0.0.1")
is false).
} | ||
|
||
// check if the current server URL in the adopted cluster kubeconfig is using | ||
// a local address, which would not work in a container |
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 there no concern for any other validity? What if the user puts junk on the command line, junk that does not fit the syntax for a URL? Or junk that is a URL but has stuff (such as a query) that should not appear in a server URL?
kubeConfig.Contexts[contextName] = &api.Context{ | ||
Cluster: context.Cluster, | ||
AuthInfo: contextName, | ||
} |
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 about the Context's Extensions?
Namespace: util.SystemNamespace, | ||
}, | ||
Type: corev1.SecretTypeOpaque, | ||
Data: map[string][]byte{util.KubeconfigSecretKeyInCluster: kubeconfig}, |
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.
Isn't this actually a not-in-cluster case (at least usually)?
// +optional | ||
BootstrapSecretRef *SecretReference `json:"bootstrapSecretRef,omitempty"` | ||
// expiration time for token of adopted cluster | ||
// +optional |
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.
When a field is optional, the doc on it needs to say what the default value is. And that default needs to be implemented (e.g., by a kubebuilder comment).
Backend BackendDBType `json:"backend,omitempty"` | ||
// BootstrapSecretRef contains a reference to the kubeconfig used to bootstrap adoption of | ||
// an external cluster | ||
// +optional |
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 an "inline union": some unconditional fields plus a discriminator (Type
) that determines whether some of the other fields should be present or absent. FYI, these are frowned upon in the Kubernetes API design community. They prefer pure unions: a pure union struct has only a discriminator plus fields whose proper presence is determined by the discriminator.
Namespace: util.SystemNamespace, | ||
InClusterKey: util.KubeconfigSecretKeyInCluster, | ||
} | ||
} |
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 sets cp.Spec.AdoptedTokenExpirationSeconds
?
split := strings.SplitN(pair, "=", 2) | ||
if len(split) == 2 { | ||
params[split[0]] = split[1] | ||
} |
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 the user should be given an error message if len(split) != 2
.
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.
Finished a review. Left individual comments.
Summary
Adds support for importing (adopting) external cluster as control plane
Related issue(s)
Fixes #163