Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
65 changes: 65 additions & 0 deletions cmd/cluster-bootstrap/bootstrapinplace.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package main

import (
"errors"

"github.com/openshift/cluster-bootstrap/pkg/bootstrapinplace"

"github.com/spf13/cobra"
)

var (
CmdBootstrapInPlace = &cobra.Command{
Use: "bootstrap-in-place",
Short: "Create Ignition based on Fedora CoreOS Config",
Long: "",
PreRunE: validateBootstrapInPlaceOpts,
RunE: runCmdBootstrapInPlace,
SilenceUsage: true,
}

bootstrapInPlaceOpts struct {
assetDir string
ignitionPath string
input string
Strict bool
Pretty bool
}
)

func init() {
cmdRoot.AddCommand(CmdBootstrapInPlace)
CmdBootstrapInPlace.Flags().BoolVarP(&bootstrapInPlaceOpts.Strict, "strict", "s", true, "fail on any warning")
CmdBootstrapInPlace.Flags().BoolVarP(&bootstrapInPlaceOpts.Pretty, "pretty", "p", true, "output formatted json")
CmdBootstrapInPlace.Flags().StringVar(&bootstrapInPlaceOpts.input, "input", "", "fcc input file path")
CmdBootstrapInPlace.Flags().StringVar(&bootstrapInPlaceOpts.ignitionPath, "output", "o", "Ignition output file path")
CmdBootstrapInPlace.Flags().StringVarP(&bootstrapInPlaceOpts.assetDir, "asset-dir", "d", "", "allow embedding local files from this directory")
}

func runCmdBootstrapInPlace(cmd *cobra.Command, args []string) error {
bip, err := bootstrapinplace.NewBootstrapInPlaceCommand(bootstrapinplace.BootstrapInPlaceConfig{
AssetDir: bootstrapInPlaceOpts.assetDir,
IgnitionPath: bootstrapInPlaceOpts.ignitionPath,
Input: bootstrapInPlaceOpts.input,
Strict: bootstrapInPlaceOpts.Strict,
Pretty: bootstrapInPlaceOpts.Pretty,
})
if err != nil {
return err
}

return bip.Create()
}

func validateBootstrapInPlaceOpts(cmd *cobra.Command, args []string) error {
if bootstrapInPlaceOpts.ignitionPath == "" {
return errors.New("missing required flag: --output")
}
if bootstrapInPlaceOpts.assetDir == "" {
return errors.New("missing required flag: --asset-dir")
}
if bootstrapInPlaceOpts.input == "" {
return errors.New("missing required flag: --input")
}
return nil
}
4 changes: 4 additions & 0 deletions cmd/cluster-bootstrap/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"errors"
"strings"
"time"

"github.com/spf13/cobra"

Expand All @@ -26,6 +27,7 @@ var (
requiredPodClauses []string
waitForTearDownEvent string
earlyTearDown bool
assetsCreatedTimeout time.Duration
}
)

Expand All @@ -44,6 +46,7 @@ func init() {
cmdStart.Flags().StringSliceVar(&startOpts.requiredPodClauses, "required-pods", defaultRequiredPods, "List of pods name prefixes with their namespace (written as <namespace>/<pod-prefix>) that are required to be running and ready before the start command does the pivot, or alternatively a list of or'ed pod prefixes with a description (written as <desc>:<namespace>/<pod-prefix>|<namespace>/<pod-prefix>|...).")
cmdStart.Flags().StringVar(&startOpts.waitForTearDownEvent, "tear-down-event", "", "if this optional event name of the form <ns>/<event-name> is given, the event is waited for before tearing down the bootstrap control plane")
cmdStart.Flags().BoolVar(&startOpts.earlyTearDown, "tear-down-early", true, "tear down immediate after the non-bootstrap control plane is up and bootstrap-success event is created.")
cmdStart.Flags().DurationVar(&startOpts.assetsCreatedTimeout, "assets-create-timeout", time.Duration(60)*time.Minute, "how long to wait for all the assets be created.")
}

func runCmdStart(cmd *cobra.Command, args []string) error {
Expand All @@ -59,6 +62,7 @@ func runCmdStart(cmd *cobra.Command, args []string) error {
RequiredPodPrefixes: podPrefixes,
WaitForTearDownEvent: startOpts.waitForTearDownEvent,
EarlyTearDown: startOpts.earlyTearDown,
AssetsCreatedTimeout: startOpts.assetsCreatedTimeout,
})
if err != nil {
return err
Expand Down
71,044 changes: 71,044 additions & 0 deletions glide.diff

Large diffs are not rendered by default.

16 changes: 12 additions & 4 deletions glide.yaml
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
package: github.com/openshift/cluster-bootstrap
import:
- package: k8s.io/apimachinery
version: kubernetes-1.13.4
version: 86fb29eff6288413d76bd8506874fddd9fccdff0
- package: k8s.io/api
version: kubernetes-1.13.4
version: 5cb15d34447165a97c76ed5a60e4e99c8a01ecfe
- package: k8s.io/client-go
version: kubernetes-1.13.4
version: b40b2a5939e43f7ffe0028ad67586b7ce50bb675
- package: github.com/openshift/library-go
version: master
version: 950af653b51af28697df79f1406fc9d21f722db8
- package: github.com/json-iterator/go
version: f2b4162afba35581b6d4a50d3b8f34e33c144682
- package: google.golang.org/appengine
version: b2f4a3cf3c67576a2ee09e1fe62656a5086ce880
- package: github.com/spf13/cobra
version: c439c4fa093711d42e1b01acb1235b52004753c1
- package: github.com/spf13/pflag
version: 583c0c0531f06d5278b7d917446061adc344b5cd
- package: github.com/coreos/fcct
version: v0.8.0
73 changes: 73 additions & 0 deletions pkg/bootstrapinplace/bootstrapinplace.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright 2019 Red Hat, Inc

package bootstrapinplace

import (
"fmt"
"io/ioutil"
"os"

"github.com/coreos/fcct/config"
fcctCommon "github.com/coreos/fcct/config/common"
)

func fail(format string, args ...interface{}) {
fmt.Printf(format, args...)
os.Exit(1)
}

type BootstrapInPlaceConfig struct {
AssetDir string
IgnitionPath string
Input string
Strict bool
Pretty bool
}
type BootstrapInPlaceCommand struct {
config BootstrapInPlaceConfig
}

func NewBootstrapInPlaceCommand(config BootstrapInPlaceConfig) (*BootstrapInPlaceCommand, error) {
return &BootstrapInPlaceCommand{
config: config,
}, nil
}

// Creating master ignition that will be used by node after reboot
// Using fcct tool (tool that takes yaml and according to it creates ignition):
// 1. Read actions yaml that has all the data needed by fcct to create master.ign
// 2. Create ignition data
// 3. Write created data to file
func (i *BootstrapInPlaceCommand) Create() error {
infile, err := os.Open(i.config.Input)
if err != nil {
fail("Error occurred while trying to open %s: %v\n", i.config.Input, err)
}
defer infile.Close()

dataIn, err := ioutil.ReadAll(infile)
if err != nil {
fail("Error occurred while trying to read %s: %v\n", infile.Name(), err)
}

dataOut, r, err := config.TranslateBytes(dataIn, fcctCommon.TranslateBytesOptions{
TranslateOptions: fcctCommon.TranslateOptions{FilesDir: i.config.AssetDir},
Pretty: i.config.Pretty,
Strict: i.config.Strict},
)
fmt.Println(r.String())
if err != nil {
fail("Error translating config: %v\n", err)
}

outfile, err := os.OpenFile(i.config.IgnitionPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
fail("Failed to open %s: %v\n", i.config.IgnitionPath, err)
}
defer outfile.Close()

if _, err := outfile.Write(append(dataOut, '\n')); err != nil {
fail("Failed to write config to %s: %v\n", outfile.Name(), err)
}
return nil
}
44 changes: 42 additions & 2 deletions pkg/start/bootstrap.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,31 @@
package start

import (
"context"
"crypto/tls"
"fmt"
"io"
"k8s.io/apimachinery/pkg/util/wait"
"net/http"
"os"
"path/filepath"
"strings"
"time"
)

type bootstrapControlPlane struct {
assetDir string
podManifestPath string
ownedManifests []string
kubeApiHost string
}

// newBootstrapControlPlane constructs a new bootstrap control plane object.
func newBootstrapControlPlane(assetDir, podManifestPath string) *bootstrapControlPlane {
func newBootstrapControlPlane(assetDir, podManifestPath string, kubeApiHost string) *bootstrapControlPlane {
return &bootstrapControlPlane{
assetDir: assetDir,
podManifestPath: podManifestPath,
kubeApiHost: kubeApiHost,
}
}

Expand All @@ -42,7 +50,39 @@ func (b *bootstrapControlPlane) Start() error {
manifestsDir := filepath.Join(b.assetDir, assetPathBootstrapManifests)
ownedManifests, err := copyDirectory(manifestsDir, b.podManifestPath, false /* overwrite */)
b.ownedManifests = ownedManifests // always copy in case of partial failure.
return err
if err != nil {
return err
}

// Wait for kube-apiserver to be available and return.
return b.waitForApi()

Choose a reason for hiding this comment

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

Can you explain what problem this is looking to solve?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

in case required-pods="" (this is the case when running bootstrap-in-place) cluster-bootstrap will fail to publish an event since kube-apiserver isn't up yet.
This should solve the problem by ensuring the kube-apiserver is available when bootstrapControlPlane.Start() returns.
I'll add it to the commit message

}

// waitForApi will wait until kube-apiserver readyz endpoint is available
func (b *bootstrapControlPlane) waitForApi() error {
UserOutput("Waiting up to %v for the Kubernetes API\n", bootstrapPodsRunningTimeout)
apiContext, cancel := context.WithTimeout(context.Background(), bootstrapPodsRunningTimeout)
defer cancel()
customTransport := http.DefaultTransport.(*http.Transport).Clone()
customTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
client := &http.Client{Transport: customTransport}
previousError := ""
err := wait.PollUntil(time.Second, func() (bool, error) {
if _, err := client.Get(fmt.Sprintf("https://%s/readyz", b.kubeApiHost)); err == nil {
UserOutput("API is up\n")
return true, nil
} else if previousError != err.Error() {
UserOutput("Still waiting for the Kubernetes API: %v \n", err)
previousError = err.Error()
}

return false, nil
}, apiContext.Done())
if err != nil {
return fmt.Errorf("time out waiting for Kubernetes API")
}

return nil
}

// Teardown brings down the bootstrap control plane and cleans up the temporary manifests and
Expand Down
23 changes: 21 additions & 2 deletions pkg/start/bootstrap_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package start

import (
"fmt"
"io/ioutil"
"math/rand"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"reflect"
"strings"
"testing"
)

Expand All @@ -13,6 +18,17 @@ var (
manifests = []string{"pod-1.yaml", "pod-2.yaml"}
)

func createTestServer() (*httptest.Server, string) {
ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

if rand.Intn(2) == 1 {
panic("Randomly created error")
}
fmt.Fprintln(w, "ok")
}))
return ts, strings.Replace(ts.URL, "https://", "", 1)
}

func setUp(t *testing.T) (assetDir, podManifestPath string) {
// Create source directories.
var err error
Expand Down Expand Up @@ -71,8 +87,11 @@ func TestBootstrapControlPlane(t *testing.T) {
assetDir, podManifestPath := setUp(t)
defer tearDown(assetDir, podManifestPath, t)

ts, url := createTestServer()
defer ts.Close()

// Create and start bootstrap control plane.
bcp := newBootstrapControlPlane(assetDir, podManifestPath)
bcp := newBootstrapControlPlane(assetDir, podManifestPath, url)
if err := bcp.Start(); err != nil {
t.Errorf("bcp.Start() = %v, want: nil", err)
}
Expand Down Expand Up @@ -117,7 +136,7 @@ func TestBootstrapControlPlaneNoOverwrite(t *testing.T) {
}

// Create and start bootstrap control plane.
bcp := newBootstrapControlPlane(assetDir, podManifestPath)
bcp := newBootstrapControlPlane(assetDir, podManifestPath, "")
if err := bcp.Start(); err == nil {
t.Errorf("bcp.Start() = %v, want: non-nil", err)
}
Expand Down
Loading