Files
gograpple/grapplepatch.go

142 lines
3.3 KiB
Go

package gograpple
import (
"encoding/json"
"fmt"
"os"
"path"
"github.com/foomo/gograpple/bindata"
)
type Mount struct {
HostPath string
MountPath string
}
type patchValues struct {
Label string
Deployment string
Container string
ConfigMapMount string
Mounts []Mount
Image string
}
func newPatchValues(deployment, container string, mounts []Mount) *patchValues {
return &patchValues{
Label: defaultPatchedLabel,
Deployment: deployment,
Container: container,
ConfigMapMount: defaultConfigMapMount,
Mounts: mounts,
Image: defaultPatchImage,
}
}
func (g Grapple) Patch(image, tag, container string, mounts []Mount) error {
if g.isPatched() {
g.l.Warn("deployment already patched, rolling back first")
if err := g.rollbackUntilUnpatched(); err != nil {
return err
}
}
if err := g.validateContainer(&container); err != nil {
return err
}
if err := g.validateImage(container, &image, &tag); err != nil {
return err
}
g.l.Infof("creating a ConfigMap with deployment data")
bs, err := json.Marshal(g.deployment)
if err != nil {
return err
}
data := map[string]string{defaultConfigMapDeploymentKey: string(bs)}
_, err = g.kubeCmd.CreateConfigMap(g.deployment.Name, data)
if err != nil {
return err
}
g.l.Infof("waiting for deployment to get ready")
_, err = g.kubeCmd.WaitForRollout(g.deployment.Name, defaultWaitTimeout).Run()
if err != nil {
return err
}
g.l.Infof("extracting patch files")
const patchFolder = "the-hook"
if err := bindata.RestoreAssets(os.TempDir(), patchFolder); err != nil {
return err
}
theHookPath := path.Join(os.TempDir(), patchFolder)
g.l.Infof("building patch image with %v:%v", image, tag)
_, err = g.dockerCmd.Build(theHookPath, "--build-arg",
fmt.Sprintf("IMAGE=%v:%v", image, tag), "-t", defaultPatchImage).Run()
if err != nil {
return err
}
g.l.Infof("rendering deployment patch template")
patch, err := renderTemplate(
path.Join(theHookPath, devDeploymentPatchFile),
newPatchValues(g.deployment.Name, container, mounts),
)
if err != nil {
return err
}
g.l.Infof("patching deployment for development")
_, err = g.kubeCmd.PatchDeployment(patch, g.deployment.Name).Run()
return err
}
func (g *Grapple) Rollback() error {
if !g.isPatched() {
return fmt.Errorf("deployment not patched, stopping rollback")
}
return g.rollbackUntilUnpatched()
}
func (g Grapple) isPatched() bool {
_, ok := g.deployment.Spec.Template.ObjectMeta.Labels[defaultPatchedLabel]
return ok
}
func (g *Grapple) rollbackUntilUnpatched() error {
if !g.isPatched() {
return nil
}
if err := g.rollback(); err != nil {
return err
}
if err := g.updateDeployment(); err != nil {
return err
}
return g.rollbackUntilUnpatched()
}
func (g Grapple) rollback() error {
g.l.Infof("removing ConfigMap %v", g.deployment.Name)
_, err := g.kubeCmd.DeleteConfigMap(g.deployment.Name)
if err != nil {
// may not exist
g.l.Warn(err)
}
g.l.Infof("waiting for deployment to get ready")
_, err = g.kubeCmd.WaitForRollout(g.deployment.Name, defaultWaitTimeout).Run()
if err != nil {
return err
}
g.l.Infof("rolling back deployment %v", g.deployment.Name)
_, err = g.kubeCmd.RollbackDeployment(g.deployment.Name).Run()
if err != nil {
return err
}
return nil
}