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

Initial version of clusterctl create #219

Merged
merged 1 commit into from
May 31, 2018

Conversation

jessicaochen
Copy link
Contributor

What this PR does / why we need it:
First version of clusterctl create. There is plenty room to iterate and improve (as indicated by the TODOs). However, I think it is beneficial to get a functional initial version down so iteration and improvement can happen in parallel.
#157

Note that apiserver podspec is captured from commit a1ea634 (#153).
Special notes for your reviewer:
The code for generating the gcp example files for this cluster create command is coming in the next PR.

Release note:


@kubernetes/kube-deploy-reviewers

@k8s-ci-robot k8s-ci-robot added the cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. label May 28, 2018
@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: jessicaochen

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot added size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. approved Indicates a PR has been approved by an approver from all required OWNERS files. labels May 28, 2018
@jessicaochen
Copy link
Contributor Author

/assign @k4leung4 @mkjelland

VmDriver string
VmDriver string
Provider string
KubeonfigOutput string
Copy link
Contributor

Choose a reason for hiding this comment

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

KubeConfigOutput?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

@@ -0,0 +1,334 @@
/*
Copyright 2017 The Kubernetes Authors.
Copy link
Contributor

Choose a reason for hiding this comment

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

2018

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

return string(tmplBuf.Bytes()), nil
}

const ClusterAPIAPIServerConfigTemplate = `
Copy link
Contributor

Choose a reason for hiding this comment

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

consider moving the template into a file by itself?
will make it obvious when someone is tweaking the template vs changing code

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@@ -0,0 +1,211 @@
package clusterdeployer
Copy link
Contributor

Choose a reason for hiding this comment

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

missing license

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

func (c *clusterClient) GetClusterObjects() ([]*clusterv1.Cluster, error) {
clusters := []*clusterv1.Cluster{}
// TODO: Iterate over all namespaces where we could have Cluster API Objects
clusterlist, err := c.clientSet.ClusterV1alpha1().Clusters(apiv1.NamespaceDefault).List(metav1.ListOptions{})
Copy link
Contributor

Choose a reason for hiding this comment

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

there is a apiv1.NamespaceAll if you do want to list all clusters in all namespaces.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

*Acutally punted with issue labeled.

cluster.Status.APIEndpoints = append(cluster.Status.APIEndpoints,
clusterv1.APIEndpoint{
Host: masterIP,
Port: 443,
Copy link
Contributor

Choose a reason for hiding this comment

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

consider making 443 const

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

@@ -0,0 +1,4 @@
package clusterdeployer
Copy link
Contributor

Choose a reason for hiding this comment

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

check license for all files

Copy link
Contributor

Choose a reason for hiding this comment

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

i meant copyright

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

}
}()

glog.V(2).Info("Applying Cluster API APIServer to external cluster.")
Copy link
Contributor

Choose a reason for hiding this comment

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

while the code in this method is relatively straightforward, there is a lot to keep track of.
does it make sense to add helper methods for creating objects with the external client one for the internal client?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Broke it out as much as I could while preserving flow.

}

for _, cluster := range clusters {
cluster.SetResourceVersion("")
Copy link
Contributor

Choose a reason for hiding this comment

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

can you comment on why it is necessary to set the resource version to an empty string

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

@@ -4,12 +4,18 @@ import (
"fmt"
Copy link
Contributor

Choose a reason for hiding this comment

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

run gofmt to order imports

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

return nil, err
}
defer f.Close()
f.WriteString(kubeconfig)
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing the error check for writing the string to the file on line 31.

Recommend switching from WriteString and using ioutil.WriteFile(...)

There is a subtle bug here where it is possible that the Write on line 31 was not flushed to disk and then on line 32 the contents of the file are being used. If ioutil.WriteFile(...) is used then file handle it opens is guaranteed to be closed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

return certParams, nil
}

func GetApiServerYaml() (string, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This public method should have associated test(s).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

glog.V(1).Info("Getting internal cluster kubeconfig.")
internalKubeconfig, err := waitForKubeconfigReady(d.provider, master)
if err != nil {
return fmt.Errorf("unable to get internal cluster kbueconfig: %v", err)
Copy link
Contributor

Choose a reason for hiding this comment

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

typo: kbueconfig -> kubeconfig

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

glog.V(2).Infof("Creating master %v", master.Name)
err = externalClient.CreateMachineObjects([]*clusterv1.Machine{master})
if err != nil {
return fmt.Errorf("unable to create master machine:%v", err)
Copy link
Contributor

Choose a reason for hiding this comment

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

typo: missing space after 'machine:'

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

// Fetch freshly created master.
updatedMachines, err := externalClient.GetMachineObjects()
if err != nil {
return fmt.Errorf("unable to fetch machines:%v", err)
Copy link
Contributor

Choose a reason for hiding this comment

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

typo: missing space after 'machine:'

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

}
master, _, err = splitMachineRoles(updatedMachines)
if err != nil {
return fmt.Errorf("unable to fetch master machine:%v", err)
Copy link
Contributor

Choose a reason for hiding this comment

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

typo: missing space after 'machine:'

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.


func (d *ClusterDeployer) writeKubeconfig(kubeconfig string) error {
os.Remove(d.kubeconfigOutput)
f, err := os.Create(d.kubeconfigOutput)
Copy link
Contributor

Choose a reason for hiding this comment

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

This function, lines 223 -> 229, can be replaced / simplified with:

  return ioutil.WriteFile(d.kubeconfigOutput, []byte(kubeconfig), 0666

which is functionally equivalent (os.Create / ioutil.WriteFile both truncuate).

if err != nil {
return err
}
err = waitForMachineReady(c.clientSet, createdMachine)
Copy link
Contributor

Choose a reason for hiding this comment

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

This could be parallelized and It would be faster, if the it were to first create all machine objects. After the creationg loop completed it could then call waitForMachineReady(...) on each object.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Punted as a post v0 optimization.

Copy link
Contributor

Choose a reason for hiding this comment

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

Can you add a TODO into the code?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

defer func() {
err := externalClient.Close()
if err != nil {
glog.Error("Could not close external client")
Copy link
Contributor

Choose a reason for hiding this comment

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

Add err value to error message

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

defer func() {
err := internalClient.Close()
if err != nil {
glog.Error("Could not close internal client")
Copy link
Contributor

Choose a reason for hiding this comment

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

Add err value to error message

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

}

func (d *ClusterDeployer) applyClusterAPIApiserver(client ClusterClient) error {
yaml, err := getApiServerYaml()
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: I would prefer if this was called getClusterApiServerYaml since it could mean the core api server.

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 think whether it uses yaml under the covers or something else is a implementation detail. I was hoping the clusterapi + apiserver in the method name would make it clear it is the cluster api apiserver as opposed to the main apiserver. Is this ok or do you have other ideas on how do distinguish between the main apiserver and the clusterapi apiserver for the method name?

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, that makes sense!

CreateClusterObject(*clusterv1.Cluster) error
CreateMachineObjects([]*clusterv1.Machine) error
UpdateClusterObjectEndpoint(string) error
Close() error
Copy link
Contributor

Choose a reason for hiding this comment

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

Eventually, will there be Delete methods for Machine and Cluster? Is that out of scope for clusterctl?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Deletion is in scope for clusterctl.

If that ability is added to the ClusterDeployer, it would make sense to extend the ClusterClient interface to handle the needed methods at that point.

@@ -22,13 +22,10 @@ $ go build

Copy link
Contributor

Choose a reason for hiding this comment

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

Can you add a TBD here so that it's clear that the limitations section isn't filled out? Right now it is easy to think that Creating a cluster falls in the limitations section.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

"k8s.io/client-go/util/cert/triple"
)

var apiServerImage = "gcr.io/k8s-cluster-api/cluster-apiserver:0.0.3"
Copy link
Contributor

Choose a reason for hiding this comment

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

This is out of date compared to head. I think the current version is 0.0.5 or so.

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 know. This is snapshot-ed to the version called out in the PR description. My plan is to move roll forward after landing this v0 PR. There is a lot of development in this repo (a good thing :) ) and I think it is better to land something working and improve it than hold off trying to keep up with all the development.

var apiServerImage = "gcr.io/k8s-cluster-api/cluster-apiserver:0.0.3"

func init() {
if img, ok := os.LookupEnv("CLUSTER_API_SERVER_IMAGE"); ok {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why an env var instead of a flag?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is what we have been doing so far in this repo. We can change it to a flag post v0 if folks really prefer a flag over a environment variable for a development hook.

api: clusterapi
apiserver: "true"
---
apiVersion: apps/v1beta1
Copy link
Contributor

Choose a reason for hiding this comment

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

Deployments should use v1 instead of v1beta1.

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 is snapshot-ed to the version called out in the PR description. I want to check it in with a consistent working snapshot so we know what changes it is caught up with and what changes it has yet to catch up on.

name: default
namespace: default
---
apiVersion: apps/v1beta1
Copy link
Contributor

Choose a reason for hiding this comment

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

same 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.

This is snapshot-ed to the version called out in the PR description. I want to check it in with a consistent working snapshot so we know what changes it is caught up with and what changes it has yet to catch up on.

if err != nil {
return err
}
err = waitForMachineReady(c.clientSet, createdMachine)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you add a TODO into the code?

**NOT YET SUPPORTED!** - Use [provider-specific deployer](../README.md) to create clusters till cluster creation is supported.

1. Create a `cluster.yaml` and `machines.yaml` files configured for your cluster. See the provider specific templates and generation tools at `$GOPATH/src/sigs.k8s.io/cluster-api/clusterctl/examples/<provider>`.
1. Create a `cluster.yaml`, `machines.yaml` and `provider-components.yaml` files configured for your cluster. See the provider specific templates and generation tools at `$GOPATH/src/sigs.k8s.io/cluster-api/clusterctl/examples/<provider>`.
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 intend to include the example provider-components.yaml in this PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is imminently coming in the next PR.

@k4leung4
Copy link
Contributor

lgtm

@mkjelland
Copy link
Contributor

/lgtm

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label May 31, 2018
@k8s-ci-robot k8s-ci-robot merged commit cf9bcf6 into kubernetes-sigs:master May 31, 2018
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. 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.

6 participants