|
|
@ -22,10 +22,10 @@ import (
|
|
|
|
goerrors "errors"
|
|
|
|
goerrors "errors"
|
|
|
|
"fmt"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"io"
|
|
|
|
"log"
|
|
|
|
|
|
|
|
"strings"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
log "github.com/Sirupsen/logrus"
|
|
|
|
jsonpatch "github.com/evanphx/json-patch"
|
|
|
|
jsonpatch "github.com/evanphx/json-patch"
|
|
|
|
"k8s.io/kubernetes/pkg/api"
|
|
|
|
"k8s.io/kubernetes/pkg/api"
|
|
|
|
"k8s.io/kubernetes/pkg/api/errors"
|
|
|
|
"k8s.io/kubernetes/pkg/api/errors"
|
|
|
@ -74,6 +74,13 @@ type ResourceActorFunc func(*resource.Info) error
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// Namespace will set the namespace
|
|
|
|
// Namespace will set the namespace
|
|
|
|
func (c *Client) Create(namespace string, reader io.Reader, timeout int64, shouldWait bool) error {
|
|
|
|
func (c *Client) Create(namespace string, reader io.Reader, timeout int64, shouldWait bool) error {
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "Create",
|
|
|
|
|
|
|
|
"namespace": namespace,
|
|
|
|
|
|
|
|
"timeout": timeout,
|
|
|
|
|
|
|
|
"wait": shouldWait,
|
|
|
|
|
|
|
|
}).Debug("Beginning resource creation")
|
|
|
|
client, err := c.ClientSet()
|
|
|
|
client, err := c.ClientSet()
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
return err
|
|
|
@ -81,10 +88,19 @@ func (c *Client) Create(namespace string, reader io.Reader, timeout int64, shoul
|
|
|
|
if err := ensureNamespace(client, namespace); err != nil {
|
|
|
|
if err := ensureNamespace(client, namespace); err != nil {
|
|
|
|
return err
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "Create",
|
|
|
|
|
|
|
|
}).Debug("Building manifests")
|
|
|
|
infos, buildErr := c.BuildUnstructured(namespace, reader)
|
|
|
|
infos, buildErr := c.BuildUnstructured(namespace, reader)
|
|
|
|
if buildErr != nil {
|
|
|
|
if buildErr != nil {
|
|
|
|
return buildErr
|
|
|
|
return buildErr
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "Create",
|
|
|
|
|
|
|
|
"num-manifests": len(infos),
|
|
|
|
|
|
|
|
}).Debug("Creating manifests")
|
|
|
|
if err := perform(c, namespace, infos, createResource); err != nil {
|
|
|
|
if err := perform(c, namespace, infos, createResource); err != nil {
|
|
|
|
return err
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -97,7 +113,11 @@ func (c *Client) Create(namespace string, reader io.Reader, timeout int64, shoul
|
|
|
|
func (c *Client) newBuilder(namespace string, reader io.Reader) *resource.Result {
|
|
|
|
func (c *Client) newBuilder(namespace string, reader io.Reader) *resource.Result {
|
|
|
|
schema, err := c.Validator(true, c.SchemaCacheDir)
|
|
|
|
schema, err := c.Validator(true, c.SchemaCacheDir)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("warning: failed to load schema: %s", err)
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "newBuilder",
|
|
|
|
|
|
|
|
"error": err,
|
|
|
|
|
|
|
|
}).Error("Failed to load schema")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return c.NewBuilder().
|
|
|
|
return c.NewBuilder().
|
|
|
|
ContinueOnError().
|
|
|
|
ContinueOnError().
|
|
|
@ -111,14 +131,26 @@ func (c *Client) newBuilder(namespace string, reader io.Reader) *resource.Result
|
|
|
|
|
|
|
|
|
|
|
|
// BuildUnstructured validates for Kubernetes objects and returns unstructured infos.
|
|
|
|
// BuildUnstructured validates for Kubernetes objects and returns unstructured infos.
|
|
|
|
func (c *Client) BuildUnstructured(namespace string, reader io.Reader) (Result, error) {
|
|
|
|
func (c *Client) BuildUnstructured(namespace string, reader io.Reader) (Result, error) {
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "BuildUnstructured",
|
|
|
|
|
|
|
|
}).Debug("Beginning unstructured object build")
|
|
|
|
schema, err := c.Validator(true, c.SchemaCacheDir)
|
|
|
|
schema, err := c.Validator(true, c.SchemaCacheDir)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("warning: failed to load schema: %s", err)
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "BuildUnstructured",
|
|
|
|
|
|
|
|
"error": err,
|
|
|
|
|
|
|
|
}).Error("Failed to load schema")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
mapper, typer, err := c.UnstructuredObject()
|
|
|
|
mapper, typer, err := c.UnstructuredObject()
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("failed to load mapper: %s", err)
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "BuildUnstructured",
|
|
|
|
|
|
|
|
"error": err,
|
|
|
|
|
|
|
|
}).Error("Failed to load mapper")
|
|
|
|
return nil, err
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var result Result
|
|
|
|
var result Result
|
|
|
@ -135,6 +167,10 @@ func (c *Client) BuildUnstructured(namespace string, reader io.Reader) (Result,
|
|
|
|
|
|
|
|
|
|
|
|
// Build validates for Kubernetes objects and returns resource Infos from a io.Reader.
|
|
|
|
// Build validates for Kubernetes objects and returns resource Infos from a io.Reader.
|
|
|
|
func (c *Client) Build(namespace string, reader io.Reader) (Result, error) {
|
|
|
|
func (c *Client) Build(namespace string, reader io.Reader) (Result, error) {
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "Build",
|
|
|
|
|
|
|
|
}).Debug("Beginning structured object build")
|
|
|
|
var result Result
|
|
|
|
var result Result
|
|
|
|
result, err := c.newBuilder(namespace, reader).Infos()
|
|
|
|
result, err := c.newBuilder(namespace, reader).Infos()
|
|
|
|
return result, scrubValidationError(err)
|
|
|
|
return result, scrubValidationError(err)
|
|
|
@ -144,6 +180,11 @@ func (c *Client) Build(namespace string, reader io.Reader) (Result, error) {
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// Namespace will set the namespace
|
|
|
|
// Namespace will set the namespace
|
|
|
|
func (c *Client) Get(namespace string, reader io.Reader) (string, error) {
|
|
|
|
func (c *Client) Get(namespace string, reader io.Reader) (string, error) {
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "Get",
|
|
|
|
|
|
|
|
"namespace": namespace,
|
|
|
|
|
|
|
|
}).Debug("Beginning resource get")
|
|
|
|
// Since we don't know what order the objects come in, let's group them by the types, so
|
|
|
|
// Since we don't know what order the objects come in, let's group them by the types, so
|
|
|
|
// that when we print them, they come looking good (headers apply to subgroups, etc.)
|
|
|
|
// that when we print them, they come looking good (headers apply to subgroups, etc.)
|
|
|
|
objs := make(map[string][]runtime.Object)
|
|
|
|
objs := make(map[string][]runtime.Object)
|
|
|
@ -152,7 +193,11 @@ func (c *Client) Get(namespace string, reader io.Reader) (string, error) {
|
|
|
|
return "", err
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
err = perform(c, namespace, infos, func(info *resource.Info) error {
|
|
|
|
err = perform(c, namespace, infos, func(info *resource.Info) error {
|
|
|
|
log.Printf("Doing get for: '%s'", info.Name)
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "Get",
|
|
|
|
|
|
|
|
"name": info.Name,
|
|
|
|
|
|
|
|
}).Info("Getting object")
|
|
|
|
obj, err := resource.NewHelper(info.Client, info.Mapping).Get(info.Namespace, info.Name, info.Export)
|
|
|
|
obj, err := resource.NewHelper(info.Client, info.Mapping).Get(info.Namespace, info.Name, info.Export)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
return err
|
|
|
@ -160,7 +205,12 @@ func (c *Client) Get(namespace string, reader io.Reader) (string, error) {
|
|
|
|
// We need to grab the ObjectReference so we can correctly group the objects.
|
|
|
|
// We need to grab the ObjectReference so we can correctly group the objects.
|
|
|
|
or, err := api.GetReference(obj)
|
|
|
|
or, err := api.GetReference(obj)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("FAILED GetReference for: %#v\n%v", obj, err)
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "Get",
|
|
|
|
|
|
|
|
"error": err,
|
|
|
|
|
|
|
|
"object": fmt.Sprintf("%#v", obj),
|
|
|
|
|
|
|
|
}).Error("FAILED GetReference")
|
|
|
|
return err
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -174,6 +224,10 @@ func (c *Client) Get(namespace string, reader io.Reader) (string, error) {
|
|
|
|
return "", err
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "Get",
|
|
|
|
|
|
|
|
}).Debug("Grouping returned resources")
|
|
|
|
// Ok, now we have all the objects grouped by types (say, by v1/Pod, v1/Service, etc.), so
|
|
|
|
// Ok, now we have all the objects grouped by types (say, by v1/Pod, v1/Service, etc.), so
|
|
|
|
// spin through them and print them. Printer is cool since it prints the header only when
|
|
|
|
// spin through them and print them. Printer is cool since it prints the header only when
|
|
|
|
// an object type changes, so we can just rely on that. Problem is it doesn't seem to keep
|
|
|
|
// an object type changes, so we can just rely on that. Problem is it doesn't seem to keep
|
|
|
@ -186,7 +240,12 @@ func (c *Client) Get(namespace string, reader io.Reader) (string, error) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, o := range ot {
|
|
|
|
for _, o := range ot {
|
|
|
|
if err := p.PrintObj(o, buf); err != nil {
|
|
|
|
if err := p.PrintObj(o, buf); err != nil {
|
|
|
|
log.Printf("failed to print object type '%s', object: '%s' :\n %v", t, o, err)
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "Get",
|
|
|
|
|
|
|
|
"type": t,
|
|
|
|
|
|
|
|
"object": o,
|
|
|
|
|
|
|
|
}).Error("Failed to print")
|
|
|
|
return "", err
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -204,45 +263,102 @@ func (c *Client) Get(namespace string, reader io.Reader) (string, error) {
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// Namespace will set the namespaces
|
|
|
|
// Namespace will set the namespaces
|
|
|
|
func (c *Client) Update(namespace string, originalReader, targetReader io.Reader, recreate bool, timeout int64, shouldWait bool) error {
|
|
|
|
func (c *Client) Update(namespace string, originalReader, targetReader io.Reader, recreate bool, timeout int64, shouldWait bool) error {
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "Update",
|
|
|
|
|
|
|
|
"namespace": namespace,
|
|
|
|
|
|
|
|
"timeout": timeout,
|
|
|
|
|
|
|
|
"recreate": recreate,
|
|
|
|
|
|
|
|
"wait": shouldWait,
|
|
|
|
|
|
|
|
}).Debug("Beginning resource update")
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "Update",
|
|
|
|
|
|
|
|
}).Debug("Rebuilding original objects")
|
|
|
|
original, err := c.BuildUnstructured(namespace, originalReader)
|
|
|
|
original, err := c.BuildUnstructured(namespace, originalReader)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed decoding reader into objects: %s", err)
|
|
|
|
return fmt.Errorf("failed decoding reader into objects: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "Update",
|
|
|
|
|
|
|
|
}).Debug("Building new objects")
|
|
|
|
target, err := c.BuildUnstructured(namespace, targetReader)
|
|
|
|
target, err := c.BuildUnstructured(namespace, targetReader)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed decoding reader into objects: %s", err)
|
|
|
|
return fmt.Errorf("failed decoding reader into objects: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
updateErrors := []string{}
|
|
|
|
updateErrors := []string{}
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "Update",
|
|
|
|
|
|
|
|
"num-objects": len(target),
|
|
|
|
|
|
|
|
}).Debug("Visiting new objects for update")
|
|
|
|
err = target.Visit(func(info *resource.Info, err error) error {
|
|
|
|
err = target.Visit(func(info *resource.Info, err error) error {
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
kind := info.Mapping.GroupVersionKind.Kind
|
|
|
|
helper := resource.NewHelper(info.Client, info.Mapping)
|
|
|
|
helper := resource.NewHelper(info.Client, info.Mapping)
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "Update",
|
|
|
|
|
|
|
|
"name": info.Name,
|
|
|
|
|
|
|
|
"kind": kind,
|
|
|
|
|
|
|
|
"namespace": info.Namespace,
|
|
|
|
|
|
|
|
}).Debug("Attempting to get resource")
|
|
|
|
if _, err := helper.Get(info.Namespace, info.Name, info.Export); err != nil {
|
|
|
|
if _, err := helper.Get(info.Namespace, info.Name, info.Export); err != nil {
|
|
|
|
if !errors.IsNotFound(err) {
|
|
|
|
if !errors.IsNotFound(err) {
|
|
|
|
return fmt.Errorf("Could not get information about the resource: err: %s", err)
|
|
|
|
return fmt.Errorf("Could not get information about the resource: err: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Since the resource does not exist, create it.
|
|
|
|
// Since the resource does not exist, create it.
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "Update",
|
|
|
|
|
|
|
|
"name": info.Name,
|
|
|
|
|
|
|
|
"kind": kind,
|
|
|
|
|
|
|
|
"namespace": info.Namespace,
|
|
|
|
|
|
|
|
}).Debug("Resource did not exist, attempting to create")
|
|
|
|
if err := createResource(info); err != nil {
|
|
|
|
if err := createResource(info); err != nil {
|
|
|
|
return fmt.Errorf("failed to create resource: %s", err)
|
|
|
|
return fmt.Errorf("failed to create resource: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
kind := info.Mapping.GroupVersionKind.Kind
|
|
|
|
"_module": "client",
|
|
|
|
log.Printf("Created a new %s called %s\n", kind, info.Name)
|
|
|
|
"_context": "Update",
|
|
|
|
|
|
|
|
"name": info.Name,
|
|
|
|
|
|
|
|
"kind": kind,
|
|
|
|
|
|
|
|
"namespace": info.Namespace,
|
|
|
|
|
|
|
|
}).Info("Created new resource")
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "Update",
|
|
|
|
|
|
|
|
"name": info.Name,
|
|
|
|
|
|
|
|
"kind": kind,
|
|
|
|
|
|
|
|
"namespace": info.Namespace,
|
|
|
|
|
|
|
|
}).Debug("Getting original resource")
|
|
|
|
originalInfo := original.Get(info)
|
|
|
|
originalInfo := original.Get(info)
|
|
|
|
if originalInfo == nil {
|
|
|
|
if originalInfo == nil {
|
|
|
|
return fmt.Errorf("no resource with the name %s found", info.Name)
|
|
|
|
return fmt.Errorf("no resource with the name %s found", info.Name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "Update",
|
|
|
|
|
|
|
|
"name": info.Name,
|
|
|
|
|
|
|
|
"kind": kind,
|
|
|
|
|
|
|
|
"namespace": info.Namespace,
|
|
|
|
|
|
|
|
}).Debug("Beginning resource update")
|
|
|
|
if err := updateResource(c, info, originalInfo.Object, recreate); err != nil {
|
|
|
|
if err := updateResource(c, info, originalInfo.Object, recreate); err != nil {
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "Update",
|
|
|
|
|
|
|
|
"name": info.Name,
|
|
|
|
|
|
|
|
"kind": kind,
|
|
|
|
|
|
|
|
"namespace": info.Namespace,
|
|
|
|
|
|
|
|
"error": err,
|
|
|
|
|
|
|
|
}).Error("Error updating resource")
|
|
|
|
log.Printf("error updating the resource %s:\n\t %v", info.Name, err)
|
|
|
|
log.Printf("error updating the resource %s:\n\t %v", info.Name, err)
|
|
|
|
updateErrors = append(updateErrors, err.Error())
|
|
|
|
updateErrors = append(updateErrors, err.Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -258,8 +374,22 @@ func (c *Client) Update(namespace string, originalReader, targetReader io.Reader
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for _, info := range original.Difference(target) {
|
|
|
|
for _, info := range original.Difference(target) {
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "Update",
|
|
|
|
|
|
|
|
"name": info.Name,
|
|
|
|
|
|
|
|
"kind": info.Mapping.GroupVersionKind.Kind,
|
|
|
|
|
|
|
|
"namespace": info.Namespace,
|
|
|
|
|
|
|
|
}).Debug("Deleting resource that no longer in manifest")
|
|
|
|
log.Printf("Deleting %s in %s...", info.Name, info.Namespace)
|
|
|
|
log.Printf("Deleting %s in %s...", info.Name, info.Namespace)
|
|
|
|
if err := deleteResource(c, info); err != nil {
|
|
|
|
if err := deleteResource(c, info); err != nil {
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "Update",
|
|
|
|
|
|
|
|
"name": info.Name,
|
|
|
|
|
|
|
|
"kind": info.Mapping.GroupVersionKind.Kind,
|
|
|
|
|
|
|
|
"namespace": info.Namespace,
|
|
|
|
|
|
|
|
}).Error("Failed to delete resource")
|
|
|
|
log.Printf("Failed to delete %s, err: %s", info.Name, err)
|
|
|
|
log.Printf("Failed to delete %s, err: %s", info.Name, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -273,12 +403,28 @@ func (c *Client) Update(namespace string, originalReader, targetReader io.Reader
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// Namespace will set the namespace
|
|
|
|
// Namespace will set the namespace
|
|
|
|
func (c *Client) Delete(namespace string, reader io.Reader) error {
|
|
|
|
func (c *Client) Delete(namespace string, reader io.Reader) error {
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "Delete",
|
|
|
|
|
|
|
|
"namespace": namespace,
|
|
|
|
|
|
|
|
}).Debug("Beginning resource delete")
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "Delete",
|
|
|
|
|
|
|
|
"namespace": namespace,
|
|
|
|
|
|
|
|
}).Debug("Rebuilding objects from manifest")
|
|
|
|
infos, err := c.BuildUnstructured(namespace, reader)
|
|
|
|
infos, err := c.BuildUnstructured(namespace, reader)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return perform(c, namespace, infos, func(info *resource.Info) error {
|
|
|
|
return perform(c, namespace, infos, func(info *resource.Info) error {
|
|
|
|
log.Printf("Starting delete for %s %s", info.Name, info.Mapping.GroupVersionKind.Kind)
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "Delete",
|
|
|
|
|
|
|
|
"namespace": namespace,
|
|
|
|
|
|
|
|
"name": info.Name,
|
|
|
|
|
|
|
|
"kind": info.Mapping.GroupVersionKind.Kind,
|
|
|
|
|
|
|
|
}).Info("Starting delete")
|
|
|
|
err := deleteResource(c, info)
|
|
|
|
err := deleteResource(c, info)
|
|
|
|
return skipIfNotFound(err)
|
|
|
|
return skipIfNotFound(err)
|
|
|
|
})
|
|
|
|
})
|
|
|
@ -286,7 +432,11 @@ func (c *Client) Delete(namespace string, reader io.Reader) error {
|
|
|
|
|
|
|
|
|
|
|
|
func skipIfNotFound(err error) error {
|
|
|
|
func skipIfNotFound(err error) error {
|
|
|
|
if errors.IsNotFound(err) {
|
|
|
|
if errors.IsNotFound(err) {
|
|
|
|
log.Printf("%v", err)
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "skipIfNotFound",
|
|
|
|
|
|
|
|
"error": err,
|
|
|
|
|
|
|
|
}).Info("Skipping not found object")
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
return err
|
|
|
@ -311,6 +461,17 @@ func watchTimeout(t time.Duration) ResourceActorFunc {
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// Handling for other kinds will be added as necessary.
|
|
|
|
// Handling for other kinds will be added as necessary.
|
|
|
|
func (c *Client) WatchUntilReady(namespace string, reader io.Reader, timeout int64, shouldWait bool) error {
|
|
|
|
func (c *Client) WatchUntilReady(namespace string, reader io.Reader, timeout int64, shouldWait bool) error {
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "WatchUntilReady",
|
|
|
|
|
|
|
|
"namespace": namespace,
|
|
|
|
|
|
|
|
"timeout": timeout,
|
|
|
|
|
|
|
|
}).Debug("Beginning watch of Jobs")
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "WatchUntilReady",
|
|
|
|
|
|
|
|
"namespace": namespace,
|
|
|
|
|
|
|
|
}).Debug("Building resources")
|
|
|
|
infos, err := c.Build(namespace, reader)
|
|
|
|
infos, err := c.Build(namespace, reader)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
return err
|
|
|
@ -346,19 +507,44 @@ func deleteResource(c *Client, info *resource.Info) error {
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
// If there is no reaper for this resources, delete it.
|
|
|
|
// If there is no reaper for this resources, delete it.
|
|
|
|
if kubectl.IsNoSuchReaperError(err) {
|
|
|
|
if kubectl.IsNoSuchReaperError(err) {
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "deleteResource",
|
|
|
|
|
|
|
|
"name": info.Name,
|
|
|
|
|
|
|
|
"namespace": info.Namespace,
|
|
|
|
|
|
|
|
}).Info("Resource does not have reaper. Deleting")
|
|
|
|
return resource.NewHelper(info.Client, info.Mapping).Delete(info.Namespace, info.Name)
|
|
|
|
return resource.NewHelper(info.Client, info.Mapping).Delete(info.Namespace, info.Name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
log.Printf("Using reaper for deleting %s", info.Name)
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "deleteResource",
|
|
|
|
|
|
|
|
"name": info.Name,
|
|
|
|
|
|
|
|
"namespace": info.Namespace,
|
|
|
|
|
|
|
|
}).Info("Using reaper for delete")
|
|
|
|
return reaper.Stop(info.Namespace, info.Name, 0, nil)
|
|
|
|
return reaper.Stop(info.Namespace, info.Name, 0, nil)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func createPatch(mapping *meta.RESTMapping, target, current runtime.Object) ([]byte, api.PatchType, error) {
|
|
|
|
func createPatch(mapping *meta.RESTMapping, target, current runtime.Object) ([]byte, api.PatchType, error) {
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "createPatch",
|
|
|
|
|
|
|
|
}).Debug("Creating patch")
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "createPatch",
|
|
|
|
|
|
|
|
"object": current,
|
|
|
|
|
|
|
|
}).Debug("Marshalling old object for merge")
|
|
|
|
oldData, err := json.Marshal(current)
|
|
|
|
oldData, err := json.Marshal(current)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return nil, api.StrategicMergePatchType, fmt.Errorf("serializing current configuration: %s", err)
|
|
|
|
return nil, api.StrategicMergePatchType, fmt.Errorf("serializing current configuration: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "createPatch",
|
|
|
|
|
|
|
|
"object": target,
|
|
|
|
|
|
|
|
}).Debug("Marshalling new object for merge")
|
|
|
|
newData, err := json.Marshal(target)
|
|
|
|
newData, err := json.Marshal(target)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return nil, api.StrategicMergePatchType, fmt.Errorf("serializing target configuration: %s", err)
|
|
|
|
return nil, api.StrategicMergePatchType, fmt.Errorf("serializing target configuration: %s", err)
|
|
|
@ -368,29 +554,53 @@ func createPatch(mapping *meta.RESTMapping, target, current runtime.Object) ([]b
|
|
|
|
return nil, api.StrategicMergePatchType, nil
|
|
|
|
return nil, api.StrategicMergePatchType, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "createPatch",
|
|
|
|
|
|
|
|
}).Debug("Attempting to get versioned object")
|
|
|
|
// Get a versioned object
|
|
|
|
// Get a versioned object
|
|
|
|
versionedObject, err := api.Scheme.New(mapping.GroupVersionKind)
|
|
|
|
versionedObject, err := api.Scheme.New(mapping.GroupVersionKind)
|
|
|
|
switch {
|
|
|
|
switch {
|
|
|
|
case runtime.IsNotRegisteredError(err):
|
|
|
|
case runtime.IsNotRegisteredError(err):
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "createPatch",
|
|
|
|
|
|
|
|
"kind": mapping.GroupVersionKind,
|
|
|
|
|
|
|
|
}).Debug("Unregistered kind found. This is likely a third party resource. Falling back to JSON merge patch")
|
|
|
|
// fall back to generic JSON merge patch
|
|
|
|
// fall back to generic JSON merge patch
|
|
|
|
patch, err := jsonpatch.CreateMergePatch(oldData, newData)
|
|
|
|
patch, err := jsonpatch.CreateMergePatch(oldData, newData)
|
|
|
|
return patch, api.MergePatchType, err
|
|
|
|
return patch, api.MergePatchType, err
|
|
|
|
case err != nil:
|
|
|
|
case err != nil:
|
|
|
|
return nil, api.StrategicMergePatchType, fmt.Errorf("failed to get versionedObject: %s", err)
|
|
|
|
return nil, api.StrategicMergePatchType, fmt.Errorf("failed to get versionedObject: %s", err)
|
|
|
|
default:
|
|
|
|
default:
|
|
|
|
log.Printf("generating strategic merge patch for %T", target)
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "createPatch",
|
|
|
|
|
|
|
|
"kind": mapping.GroupVersionKind,
|
|
|
|
|
|
|
|
}).Info("generating strategic merge patch")
|
|
|
|
patch, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, versionedObject)
|
|
|
|
patch, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, versionedObject)
|
|
|
|
return patch, api.StrategicMergePatchType, err
|
|
|
|
return patch, api.StrategicMergePatchType, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func updateResource(c *Client, target *resource.Info, currentObj runtime.Object, recreate bool) error {
|
|
|
|
func updateResource(c *Client, target *resource.Info, currentObj runtime.Object, recreate bool) error {
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "updateResource",
|
|
|
|
|
|
|
|
"name": target.Name,
|
|
|
|
|
|
|
|
"namespace": target.Namespace,
|
|
|
|
|
|
|
|
}).Debug("Updating resource")
|
|
|
|
patch, patchType, err := createPatch(target.Mapping, target.Object, currentObj)
|
|
|
|
patch, patchType, err := createPatch(target.Mapping, target.Object, currentObj)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to create patch: %s", err)
|
|
|
|
return fmt.Errorf("failed to create patch: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if patch == nil {
|
|
|
|
if patch == nil {
|
|
|
|
log.Printf("Looks like there are no changes for %s", target.Name)
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "updateResource",
|
|
|
|
|
|
|
|
"name": target.Name,
|
|
|
|
|
|
|
|
"namespace": target.Namespace,
|
|
|
|
|
|
|
|
}).Info("Looks like there are no changes to the resource")
|
|
|
|
// This needs to happen to make sure that tiller has the latest info from the API
|
|
|
|
// This needs to happen to make sure that tiller has the latest info from the API
|
|
|
|
// Otherwise there will be no labels and other functions that use labels will panic
|
|
|
|
// Otherwise there will be no labels and other functions that use labels will panic
|
|
|
|
if err := target.Get(); err != nil {
|
|
|
|
if err := target.Get(); err != nil {
|
|
|
@ -399,6 +609,13 @@ func updateResource(c *Client, target *resource.Info, currentObj runtime.Object,
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "updateResource",
|
|
|
|
|
|
|
|
"name": target.Name,
|
|
|
|
|
|
|
|
"namespace": target.Namespace,
|
|
|
|
|
|
|
|
"patch-type": patchType,
|
|
|
|
|
|
|
|
}).Debug("Sending patch to server")
|
|
|
|
// send patch to server
|
|
|
|
// send patch to server
|
|
|
|
helper := resource.NewHelper(target.Client, target.Mapping)
|
|
|
|
helper := resource.NewHelper(target.Client, target.Mapping)
|
|
|
|
obj, err := helper.Patch(target.Namespace, target.Name, patchType, patch)
|
|
|
|
obj, err := helper.Patch(target.Namespace, target.Name, patchType, patch)
|
|
|
@ -406,6 +623,12 @@ func updateResource(c *Client, target *resource.Info, currentObj runtime.Object,
|
|
|
|
return err
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "updateResource",
|
|
|
|
|
|
|
|
"name": target.Name,
|
|
|
|
|
|
|
|
"namespace": target.Namespace,
|
|
|
|
|
|
|
|
}).Debug("Refreshing object")
|
|
|
|
target.Refresh(obj, true)
|
|
|
|
target.Refresh(obj, true)
|
|
|
|
|
|
|
|
|
|
|
|
if !recreate {
|
|
|
|
if !recreate {
|
|
|
@ -424,6 +647,12 @@ func updateResource(c *Client, target *resource.Info, currentObj runtime.Object,
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "updateResource",
|
|
|
|
|
|
|
|
"name": target.Name,
|
|
|
|
|
|
|
|
"namespace": target.Namespace,
|
|
|
|
|
|
|
|
}).Debug("Recreating pods for object")
|
|
|
|
client, _ := c.ClientSet()
|
|
|
|
client, _ := c.ClientSet()
|
|
|
|
return recreatePods(client, target.Namespace, selector)
|
|
|
|
return recreatePods(client, target.Namespace, selector)
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -448,6 +677,11 @@ func getSelectorFromObject(obj runtime.Object) (map[string]string, error) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func recreatePods(client *internalclientset.Clientset, namespace string, selector map[string]string) error {
|
|
|
|
func recreatePods(client *internalclientset.Clientset, namespace string, selector map[string]string) error {
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "recreatePods",
|
|
|
|
|
|
|
|
"selector": selector,
|
|
|
|
|
|
|
|
}).Debug("Using selector to recreate pods")
|
|
|
|
pods, err := client.Pods(namespace).List(api.ListOptions{
|
|
|
|
pods, err := client.Pods(namespace).List(api.ListOptions{
|
|
|
|
FieldSelector: fields.Everything(),
|
|
|
|
FieldSelector: fields.Everything(),
|
|
|
|
LabelSelector: labels.Set(selector).AsSelector(),
|
|
|
|
LabelSelector: labels.Set(selector).AsSelector(),
|
|
|
@ -458,7 +692,12 @@ func recreatePods(client *internalclientset.Clientset, namespace string, selecto
|
|
|
|
|
|
|
|
|
|
|
|
// Restart pods
|
|
|
|
// Restart pods
|
|
|
|
for _, pod := range pods.Items {
|
|
|
|
for _, pod := range pods.Items {
|
|
|
|
log.Printf("Restarting pod: %v/%v", pod.Namespace, pod.Name)
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "recreatePods",
|
|
|
|
|
|
|
|
"namespace": pod.Namespace,
|
|
|
|
|
|
|
|
"name": pod.Name,
|
|
|
|
|
|
|
|
}).Info("Restarting pod")
|
|
|
|
|
|
|
|
|
|
|
|
// Delete each pod for get them restarted with changed spec.
|
|
|
|
// Delete each pod for get them restarted with changed spec.
|
|
|
|
if err := client.Pods(pod.Namespace).Delete(pod.Name, api.NewPreconditionDeleteOptions(string(pod.UID))); err != nil {
|
|
|
|
if err := client.Pods(pod.Namespace).Delete(pod.Name, api.NewPreconditionDeleteOptions(string(pod.UID))); err != nil {
|
|
|
@ -475,7 +714,14 @@ func watchUntilReady(timeout time.Duration, info *resource.Info) error {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
kind := info.Mapping.GroupVersionKind.Kind
|
|
|
|
kind := info.Mapping.GroupVersionKind.Kind
|
|
|
|
log.Printf("Watching for changes to %s %s with timeout of %v", kind, info.Name, timeout)
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "watchUntilReady",
|
|
|
|
|
|
|
|
"namespace": info.Namespace,
|
|
|
|
|
|
|
|
"name": info.Name,
|
|
|
|
|
|
|
|
"kind": kind,
|
|
|
|
|
|
|
|
"timeout": timeout,
|
|
|
|
|
|
|
|
}).Info("Watching for changes")
|
|
|
|
|
|
|
|
|
|
|
|
// What we watch for depends on the Kind.
|
|
|
|
// What we watch for depends on the Kind.
|
|
|
|
// - For a Job, we watch for completion.
|
|
|
|
// - For a Job, we watch for completion.
|
|
|
@ -490,17 +736,36 @@ func watchUntilReady(timeout time.Duration, info *resource.Info) error {
|
|
|
|
// we get. We care mostly about jobs, where what we want to see is
|
|
|
|
// we get. We care mostly about jobs, where what we want to see is
|
|
|
|
// the status go into a good state. For other types, like ReplicaSet
|
|
|
|
// the status go into a good state. For other types, like ReplicaSet
|
|
|
|
// we don't really do anything to support these as hooks.
|
|
|
|
// we don't really do anything to support these as hooks.
|
|
|
|
log.Printf("Add/Modify event for %s: %v", info.Name, e.Type)
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "watchUntilReady",
|
|
|
|
|
|
|
|
"namespace": info.Namespace,
|
|
|
|
|
|
|
|
"name": info.Name,
|
|
|
|
|
|
|
|
"kind": kind,
|
|
|
|
|
|
|
|
"type": e.Type,
|
|
|
|
|
|
|
|
}).Info("Add/Modify event on watched resource")
|
|
|
|
if kind == "Job" {
|
|
|
|
if kind == "Job" {
|
|
|
|
return waitForJob(e, info.Name)
|
|
|
|
return waitForJob(e, info.Name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true, nil
|
|
|
|
return true, nil
|
|
|
|
case watch.Deleted:
|
|
|
|
case watch.Deleted:
|
|
|
|
log.Printf("Deleted event for %s", info.Name)
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "watchUntilReady",
|
|
|
|
|
|
|
|
"namespace": info.Namespace,
|
|
|
|
|
|
|
|
"name": info.Name,
|
|
|
|
|
|
|
|
"kind": kind,
|
|
|
|
|
|
|
|
}).Info("Deleted event on watched resource")
|
|
|
|
return true, nil
|
|
|
|
return true, nil
|
|
|
|
case watch.Error:
|
|
|
|
case watch.Error:
|
|
|
|
// Handle error and return with an error.
|
|
|
|
// Handle error and return with an error.
|
|
|
|
log.Printf("Error event for %s", info.Name)
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "watchUntilReady",
|
|
|
|
|
|
|
|
"namespace": info.Namespace,
|
|
|
|
|
|
|
|
"name": info.Name,
|
|
|
|
|
|
|
|
"kind": kind,
|
|
|
|
|
|
|
|
}).Error("Error event on watched resource")
|
|
|
|
return true, fmt.Errorf("Failed to deploy %s", info.Name)
|
|
|
|
return true, fmt.Errorf("Failed to deploy %s", info.Name)
|
|
|
|
default:
|
|
|
|
default:
|
|
|
|
return false, nil
|
|
|
|
return false, nil
|
|
|
@ -563,12 +828,21 @@ func (c *Client) AsVersionedObject(obj runtime.Object) (runtime.Object, error) {
|
|
|
|
// waitForResources polls to get the current status of all pods, PVCs, and Services
|
|
|
|
// waitForResources polls to get the current status of all pods, PVCs, and Services
|
|
|
|
// until all are ready or a timeout is reached
|
|
|
|
// until all are ready or a timeout is reached
|
|
|
|
func (c *Client) waitForResources(timeout time.Duration, created Result) error {
|
|
|
|
func (c *Client) waitForResources(timeout time.Duration, created Result) error {
|
|
|
|
log.Printf("beginning wait for resources with timeout of %v", timeout)
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "waitForResources",
|
|
|
|
|
|
|
|
"timeout": timeout,
|
|
|
|
|
|
|
|
"num-objects": len(created),
|
|
|
|
|
|
|
|
}).Info("Beginning wait for resources")
|
|
|
|
client, _ := c.ClientSet()
|
|
|
|
client, _ := c.ClientSet()
|
|
|
|
return wait.Poll(2*time.Second, timeout, func() (bool, error) {
|
|
|
|
return wait.Poll(2*time.Second, timeout, func() (bool, error) {
|
|
|
|
pods := []api.Pod{}
|
|
|
|
pods := []api.Pod{}
|
|
|
|
services := []api.Service{}
|
|
|
|
services := []api.Service{}
|
|
|
|
pvc := []api.PersistentVolumeClaim{}
|
|
|
|
pvc := []api.PersistentVolumeClaim{}
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "waitForResources",
|
|
|
|
|
|
|
|
}).Debug("Getting objects to watch from resource list")
|
|
|
|
for _, v := range created {
|
|
|
|
for _, v := range created {
|
|
|
|
obj, err := c.AsVersionedObject(v.Object)
|
|
|
|
obj, err := c.AsVersionedObject(v.Object)
|
|
|
|
if err != nil && !runtime.IsNotRegisteredError(err) {
|
|
|
|
if err != nil && !runtime.IsNotRegisteredError(err) {
|
|
|
@ -576,18 +850,38 @@ func (c *Client) waitForResources(timeout time.Duration, created Result) error {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
switch value := obj.(type) {
|
|
|
|
switch value := obj.(type) {
|
|
|
|
case (*v1.ReplicationController):
|
|
|
|
case (*v1.ReplicationController):
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "waitForResources",
|
|
|
|
|
|
|
|
"namespace": value.Namespace,
|
|
|
|
|
|
|
|
"name": value.Name,
|
|
|
|
|
|
|
|
"selector": value.Spec.Selector,
|
|
|
|
|
|
|
|
}).Debug("Found ReplicationController. Getting Pods")
|
|
|
|
list, err := getPods(client, value.Namespace, value.Spec.Selector)
|
|
|
|
list, err := getPods(client, value.Namespace, value.Spec.Selector)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pods = append(pods, list...)
|
|
|
|
pods = append(pods, list...)
|
|
|
|
case (*v1.Pod):
|
|
|
|
case (*v1.Pod):
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "waitForResources",
|
|
|
|
|
|
|
|
"namespace": value.Namespace,
|
|
|
|
|
|
|
|
"name": value.Name,
|
|
|
|
|
|
|
|
}).Debug("Found pod. Updating info from API")
|
|
|
|
pod, err := client.Pods(value.Namespace).Get(value.Name)
|
|
|
|
pod, err := client.Pods(value.Namespace).Get(value.Name)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pods = append(pods, *pod)
|
|
|
|
pods = append(pods, *pod)
|
|
|
|
case (*extensions.Deployment):
|
|
|
|
case (*extensions.Deployment):
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "waitForResources",
|
|
|
|
|
|
|
|
"namespace": value.Namespace,
|
|
|
|
|
|
|
|
"name": value.Name,
|
|
|
|
|
|
|
|
"selector": value.Spec.Selector.MatchLabels,
|
|
|
|
|
|
|
|
}).Debug("Found deployment. Getting ReplicaSets")
|
|
|
|
// Get the RS children first
|
|
|
|
// Get the RS children first
|
|
|
|
rs, err := client.ReplicaSets(value.Namespace).List(api.ListOptions{
|
|
|
|
rs, err := client.ReplicaSets(value.Namespace).List(api.ListOptions{
|
|
|
|
FieldSelector: fields.Everything(),
|
|
|
|
FieldSelector: fields.Everything(),
|
|
|
@ -596,6 +890,13 @@ func (c *Client) waitForResources(timeout time.Duration, created Result) error {
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "waitForResources",
|
|
|
|
|
|
|
|
"namespace": value.Namespace,
|
|
|
|
|
|
|
|
"name": value.Name,
|
|
|
|
|
|
|
|
"num-objects": len(rs.Items),
|
|
|
|
|
|
|
|
}).Debug("Found ReplicaSets from Deployment. Getting Pods")
|
|
|
|
for _, r := range rs.Items {
|
|
|
|
for _, r := range rs.Items {
|
|
|
|
list, err := getPods(client, value.Namespace, r.Spec.Selector.MatchLabels)
|
|
|
|
list, err := getPods(client, value.Namespace, r.Spec.Selector.MatchLabels)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
@ -604,30 +905,63 @@ func (c *Client) waitForResources(timeout time.Duration, created Result) error {
|
|
|
|
pods = append(pods, list...)
|
|
|
|
pods = append(pods, list...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case (*extensions.DaemonSet):
|
|
|
|
case (*extensions.DaemonSet):
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "waitForResources",
|
|
|
|
|
|
|
|
"namespace": value.Namespace,
|
|
|
|
|
|
|
|
"name": value.Name,
|
|
|
|
|
|
|
|
"selector": value.Spec.Selector.MatchLabels,
|
|
|
|
|
|
|
|
}).Debug("Found DaemonSet. Getting Pods")
|
|
|
|
list, err := getPods(client, value.Namespace, value.Spec.Selector.MatchLabels)
|
|
|
|
list, err := getPods(client, value.Namespace, value.Spec.Selector.MatchLabels)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pods = append(pods, list...)
|
|
|
|
pods = append(pods, list...)
|
|
|
|
case (*apps.StatefulSet):
|
|
|
|
case (*apps.StatefulSet):
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "waitForResources",
|
|
|
|
|
|
|
|
"namespace": value.Namespace,
|
|
|
|
|
|
|
|
"name": value.Name,
|
|
|
|
|
|
|
|
"selector": value.Spec.Selector.MatchLabels,
|
|
|
|
|
|
|
|
}).Debug("Found StatefulSet. Getting Pods")
|
|
|
|
list, err := getPods(client, value.Namespace, value.Spec.Selector.MatchLabels)
|
|
|
|
list, err := getPods(client, value.Namespace, value.Spec.Selector.MatchLabels)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pods = append(pods, list...)
|
|
|
|
pods = append(pods, list...)
|
|
|
|
case (*extensions.ReplicaSet):
|
|
|
|
case (*extensions.ReplicaSet):
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "waitForResources",
|
|
|
|
|
|
|
|
"namespace": value.Namespace,
|
|
|
|
|
|
|
|
"name": value.Name,
|
|
|
|
|
|
|
|
"selector": value.Spec.Selector.MatchLabels,
|
|
|
|
|
|
|
|
}).Debug("Found ReplicaSet. Getting Pods")
|
|
|
|
list, err := getPods(client, value.Namespace, value.Spec.Selector.MatchLabels)
|
|
|
|
list, err := getPods(client, value.Namespace, value.Spec.Selector.MatchLabels)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pods = append(pods, list...)
|
|
|
|
pods = append(pods, list...)
|
|
|
|
case (*v1.PersistentVolumeClaim):
|
|
|
|
case (*v1.PersistentVolumeClaim):
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "waitForResources",
|
|
|
|
|
|
|
|
"namespace": value.Namespace,
|
|
|
|
|
|
|
|
"name": value.Name,
|
|
|
|
|
|
|
|
}).Debug("Found PersistentVolumeClaim. Updating info from API")
|
|
|
|
claim, err := client.PersistentVolumeClaims(value.Namespace).Get(value.Name)
|
|
|
|
claim, err := client.PersistentVolumeClaims(value.Namespace).Get(value.Name)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pvc = append(pvc, *claim)
|
|
|
|
pvc = append(pvc, *claim)
|
|
|
|
case (*v1.Service):
|
|
|
|
case (*v1.Service):
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "waitForResources",
|
|
|
|
|
|
|
|
"namespace": value.Namespace,
|
|
|
|
|
|
|
|
"name": value.Name,
|
|
|
|
|
|
|
|
}).Debug("Found Service. Updating info from API")
|
|
|
|
svc, err := client.Services(value.Namespace).Get(value.Name)
|
|
|
|
svc, err := client.Services(value.Namespace).Get(value.Name)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
return false, err
|
|
|
@ -635,6 +969,13 @@ func (c *Client) waitForResources(timeout time.Duration, created Result) error {
|
|
|
|
services = append(services, *svc)
|
|
|
|
services = append(services, *svc)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "waitForResources",
|
|
|
|
|
|
|
|
"num-pods": len(pods),
|
|
|
|
|
|
|
|
"num-services": len(services),
|
|
|
|
|
|
|
|
"num-volumes": len(pvc),
|
|
|
|
|
|
|
|
}).Debug("Checking resources for ready state")
|
|
|
|
return podsReady(pods) && servicesReady(services) && volumesReady(pvc), nil
|
|
|
|
return podsReady(pods) && servicesReady(services) && volumesReady(pvc), nil
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -655,8 +996,14 @@ func waitForJob(e watch.Event, name string) (bool, error) {
|
|
|
|
return true, fmt.Errorf("Job failed: %s", c.Reason)
|
|
|
|
return true, fmt.Errorf("Job failed: %s", c.Reason)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
log.Printf("%s: Jobs active: %d, jobs failed: %d, jobs succeeded: %d", name, o.Status.Active, o.Status.Failed, o.Status.Succeeded)
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "waitForJob",
|
|
|
|
|
|
|
|
"name": name,
|
|
|
|
|
|
|
|
"active": o.Status.Active,
|
|
|
|
|
|
|
|
"failed": o.Status.Failed,
|
|
|
|
|
|
|
|
"succeeded": o.Status.Succeeded,
|
|
|
|
|
|
|
|
}).Info("Waiting on job completion")
|
|
|
|
return false, nil
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -676,6 +1023,11 @@ func scrubValidationError(err error) error {
|
|
|
|
// WaitAndGetCompletedPodPhase waits up to a timeout until a pod enters a completed phase
|
|
|
|
// WaitAndGetCompletedPodPhase waits up to a timeout until a pod enters a completed phase
|
|
|
|
// and returns said phase (PodSucceeded or PodFailed qualify)
|
|
|
|
// and returns said phase (PodSucceeded or PodFailed qualify)
|
|
|
|
func (c *Client) WaitAndGetCompletedPodPhase(namespace string, reader io.Reader, timeout time.Duration) (api.PodPhase, error) {
|
|
|
|
func (c *Client) WaitAndGetCompletedPodPhase(namespace string, reader io.Reader, timeout time.Duration) (api.PodPhase, error) {
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "WaitAndGetCompletedPodPhase",
|
|
|
|
|
|
|
|
"namespace": namespace,
|
|
|
|
|
|
|
|
}).Debug("Creating pod from manifest")
|
|
|
|
infos, err := c.Build(namespace, reader)
|
|
|
|
infos, err := c.Build(namespace, reader)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return api.PodUnknown, err
|
|
|
|
return api.PodUnknown, err
|
|
|
@ -704,8 +1056,13 @@ func watchPodUntilComplete(timeout time.Duration, info *resource.Info) error {
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.WithFields(log.Fields{
|
|
|
|
log.Printf("Watching pod %s for completion with timeout of %v", info.Name, timeout)
|
|
|
|
"_module": "client",
|
|
|
|
|
|
|
|
"_context": "watchPodUntilComplete",
|
|
|
|
|
|
|
|
"namespace": info.Namespace,
|
|
|
|
|
|
|
|
"name": info.Name,
|
|
|
|
|
|
|
|
"timeout": timeout,
|
|
|
|
|
|
|
|
}).Info("Watching pod for completion")
|
|
|
|
_, err = watch.Until(timeout, w, func(e watch.Event) (bool, error) {
|
|
|
|
_, err = watch.Until(timeout, w, func(e watch.Event) (bool, error) {
|
|
|
|
return conditions.PodCompleted(e)
|
|
|
|
return conditions.PodCompleted(e)
|
|
|
|
})
|
|
|
|
})
|
|
|
|