Skip to content

Commit

Permalink
pre-install hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
zreigz committed Nov 20, 2023
1 parent d56eda3 commit 26ffaf7
Show file tree
Hide file tree
Showing 7 changed files with 243 additions and 71 deletions.
7 changes: 6 additions & 1 deletion pkg/hook/delete_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package hook

import (
resourceutil "github.com/pluralsh/deployment-operator/pkg/sync/resource"
"github.com/pluralsh/polly/containers"

"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
Expand Down Expand Up @@ -30,5 +31,9 @@ func DeletePolicies(obj *unstructured.Unstructured) []DeletePolicy {
policies = append(policies, p)
}
}
return policies
if policies == nil {
policies = append(policies, BeforeHookCreation)
}
newSet := containers.ToSet[DeletePolicy](policies)
return newSet.List()
}
10 changes: 6 additions & 4 deletions pkg/hook/types.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package hook

import (
"github.com/pluralsh/polly/containers"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
)
Expand Down Expand Up @@ -29,8 +30,9 @@ func NewHookType(t string) (Type, bool) {
}

type Hook struct {
Weight int
Types []Type
Kind schema.ObjectKind
Object *unstructured.Unstructured
Weight int
Types containers.Set[Type]
DeletePolicies containers.Set[DeletePolicy]
Kind schema.ObjectKind
Object *unstructured.Unstructured
}
7 changes: 4 additions & 3 deletions pkg/manifests/template/template.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package template

import (
"github.com/pluralsh/deployment-operator/pkg/hook"
"os"
"path/filepath"

"github.com/pluralsh/deployment-operator/pkg/hook"

console "github.com/pluralsh/console-client-go"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/kubectl/pkg/cmd/util"
Expand All @@ -16,14 +17,14 @@ const (
RendererHelm Renderer = "helm"
RendererRaw Renderer = "raw"
RenderKustomize Renderer = "kustomize"
ChartFileName = "Chart.yaml"
ChartFileName = "Chart.yaml"
)

type Template interface {
Render(svc *console.ServiceDeploymentExtended, utilFactory util.Factory) ([]*unstructured.Unstructured, error)
}

func Render(dir string, svc *console.ServiceDeploymentExtended, utilFactory util.Factory) ([]*unstructured.Unstructured, error) {
func Render(dir string, svc *console.ServiceDeploymentExtended, utilFactory util.Factory) ([]*unstructured.Unstructured, []*unstructured.Unstructured, error) {
renderer := RendererRaw

_ = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
Expand Down
147 changes: 126 additions & 21 deletions pkg/sync/hook.go
Original file line number Diff line number Diff line change
@@ -1,30 +1,135 @@
package sync

import (
"sort"
"context"
"fmt"

"github.com/pluralsh/deployment-operator/pkg/hook"
"github.com/pluralsh/polly/containers"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/cli-utils/pkg/inventory"
)

func GetHooks(obj []*unstructured.Unstructured) []hook.Hook {
hooks := make([]hook.Hook, 0)
for _, h := range obj {
hooks = append(hooks, hook.Hook{
Weight: hook.Weight(h),
Types: hook.Types(h),
Kind: h.GetObjectKind(),
Object: h,
})
}
sort.Slice(hooks, func(i, j int) bool {
kindI := hooks[i].Kind.GroupVersionKind()
kindJ := hooks[j].Kind.GroupVersionKind()
return kindI.Kind < kindJ.Kind
})
sort.Slice(hooks, func(i, j int) bool {
return hooks[i].Weight < hooks[j].Weight
})

return hooks
func (engine *Engine) managePreInstallHooks(ctx context.Context, namespace, name, id string, hookObjects []*unstructured.Unstructured, objects []*unstructured.Unstructured) error {

Check failure on line 16 in pkg/sync/hook.go

View workflow job for this annotation

GitHub Actions / Lint

func `(*Engine).managePreInstallHooks` is unused (unused)
installed, err := engine.isInstalled(id, objects)
if err != nil {
return err
}
if installed {
return nil
}
preInstallHooks := make([]hook.Hook, 0)
hooks := GetHooks(hookObjects)
for _, h := range hooks {
if h.Types.Has(hook.PreInstall) {
preInstallHooks = append(preInstallHooks, h)
}
}

return engine.preInstallHooks(ctx, namespace, name, id, preInstallHooks)
}

func (engine *Engine) preInstallHooks(ctx context.Context, namespace, name, id string, hooks []hook.Hook) error {

Check failure on line 35 in pkg/sync/hook.go

View workflow job for this annotation

GitHub Actions / Lint

func `(*Engine).preInstallHooks` is unused (unused)
inv := inventory.WrapInventoryInfoObj(hookInventoryObjTemplate(id, hook.PreInstall))
var manifests []*unstructured.Unstructured
hookSet := containers.Set[*unstructured.Unstructured]{}
deleteBefore := containers.Set[*unstructured.Unstructured]{}
deleteFailed := containers.Set[*unstructured.Unstructured]{}
deleteSucceeded := containers.Set[*unstructured.Unstructured]{}
for _, h := range hooks {
if h.DeletePolicies.Has(hook.BeforeHookCreation) {

Check failure on line 43 in pkg/sync/hook.go

View workflow job for this annotation

GitHub Actions / Lint

ifElseChain: rewrite if-else to switch statement (gocritic)
deleteBefore.Add(h.Object)
} else if h.DeletePolicies.Has(hook.HookFailed) {
deleteFailed.Add(h.Object)
} else if h.DeletePolicies.Has(hook.HookSucceeded) {
deleteSucceeded.Add(h.Object)
}
hookSet.Add(h.Object)
}
dynamicClient, err := engine.utilFactory.DynamicClient()
if err != nil {
return err
}
client, err := engine.utilFactory.KubernetesClientSet()
if err != nil {
return err
}
invMap, err := client.CoreV1().ConfigMaps(inventoryFileNamespace).Get(ctx, getInvHookName(id, hook.PreInstall), metav1.GetOptions{})
if err != nil {
if !apierrors.IsNotFound(err) {
return err
}
}
if invMap != nil {
// delete previous resources
for _, r := range deleteBefore.List() {
gvk := r.GroupVersionKind()
gvr := schema.GroupVersionResource{
Group: gvk.Group,
Version: gvk.Version,
Resource: gvk.Kind,
}
if err := dynamicClient.Resource(gvr).Namespace(r.GetNamespace()).Delete(ctx, r.GetName(), metav1.DeleteOptions{}); err != nil {
if !apierrors.IsNotFound(err) {
return err
}
}
}
}

manifests = hookSet.List()
if len(manifests) == 0 {
return nil
}

ch := engine.applier.Run(ctx, inv, manifests, GetDefaultApplierOptions())
statsCollector, statusCollector, err := GetStatusCollector(ch, false)
if err != nil {
return err
}

if err := FormatSummary(namespace, name, *statsCollector); err != nil {
return err
}

for _, v := range statusCollector.latestStatus {
if v.Resource == nil {
continue
}

}

return nil
}

func (engine *Engine) isInstalled(id string, objects []*unstructured.Unstructured) (bool, error) {

Check failure on line 108 in pkg/sync/hook.go

View workflow job for this annotation

GitHub Actions / Lint

func `(*Engine).isInstalled` is unused (unused)
inventoryName := GetInventoryName(id)
client, err := engine.utilFactory.KubernetesClientSet()
if err != nil {
return false, err
}
invConfigMap, err := client.CoreV1().ConfigMaps(inventoryFileNamespace).Get(context.Background(), inventoryName, metav1.GetOptions{})
if err != nil {
return false, err
}
if apierrors.IsNotFound(err) {
return false, nil
}
if len(invConfigMap.Data) == len(objects) {
return true, nil
}

return false, nil
}

func hookInventoryObjTemplate(id string, t hook.Type) *unstructured.Unstructured {

Check failure on line 128 in pkg/sync/hook.go

View workflow job for this annotation

GitHub Actions / Lint

func `hookInventoryObjTemplate` is unused (unused)
name := getInvHookName(id, t)
return GenDefaultInventoryUnstructuredMap(inventoryFileNamespace, name, name)
}

func getInvHookName(id string, t hook.Type) string {

Check failure on line 133 in pkg/sync/hook.go

View workflow job for this annotation

GitHub Actions / Lint

func `getInvHookName` is unused (unused)
return fmt.Sprintf("%s-hook-%s", t, id)
}
42 changes: 5 additions & 37 deletions pkg/sync/loop.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ const (
// The field manager name for the ones agentk owns, see
// https://kubernetes.io/docs/reference/using-api/server-side-apply/#field-management
fieldManager = "application/apply-patch"

inventoryFileNamespace = "plrl-deploy-operator"
)

func (engine *Engine) ControlLoop() {
Expand Down Expand Up @@ -122,27 +124,8 @@ func (engine *Engine) processItem(item interface{}) error {
log.Error(err, "failed to check namespace")
return err
}
ch := engine.applier.Run(ctx, inv, manifests, apply.ApplierOptions{
ServerSideOptions: common.ServerSideOptions{
// It's supported since Kubernetes 1.16, so there should be no reason not to use it.
// https://kubernetes.io/docs/reference/using-api/server-side-apply/
ServerSideApply: true,
// GitOps repository is the source of truth and that's what we are applying, so overwrite any conflicts.
// https://kubernetes.io/docs/reference/using-api/server-side-apply/#conflicts
ForceConflicts: true,
// https://kubernetes.io/docs/reference/using-api/server-side-apply/#field-management
FieldManager: fieldManager,
},
ReconcileTimeout: 10 * time.Second,
// If we are not waiting for status, tell the applier to not
// emit the events.
EmitStatusEvents: true,
NoPrune: false,
DryRunStrategy: common.DryRunNone,
PrunePropagationPolicy: metav1.DeletePropagationBackground,
PruneTimeout: 20 * time.Second,
InventoryPolicy: inventory.PolicyAdoptAll,
})

ch := engine.applier.Run(ctx, inv, manifests, GetDefaultApplierOptions())

return engine.UpdateApplyStatus(id, svc.Name, svc.Namespace, ch, false, vcache)
}
Expand Down Expand Up @@ -172,20 +155,5 @@ func (engine *Engine) splitObjects(id string, objs []*unstructured.Unstructured)
}

func (engine *Engine) defaultInventoryObjTemplate(id string) (*unstructured.Unstructured, error) {
name := "inventory-" + id
namespace := "plrl-deploy-operator"

return &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": name,
"namespace": namespace,
"labels": map[string]interface{}{
common.InventoryLabel: id,
},
},
},
}, nil
return GenDefaultInventoryUnstructuredMap(inventoryFileNamespace, GetInventoryName(id), id), nil
}
20 changes: 15 additions & 5 deletions pkg/sync/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,10 +208,9 @@ func FormatActionGroupEvent(age event.ActionGroupEvent) error {
return nil
}

func (engine *Engine) UpdateApplyStatus(id, name, namespace string, ch <-chan event.Event, printStatus bool, vcache map[manifests.GroupName]string) error {
func GetStatusCollector(ch <-chan event.Event, printStatus bool) (*stats.Stats, *StatusCollector, error) {
var statsCollector stats.Stats
var err error
components := []*console.ComponentAttributes{}
statusCollector := &StatusCollector{
latestStatus: make(map[object.ObjMetadata]event.StatusEvent),
}
Expand All @@ -223,10 +222,10 @@ func (engine *Engine) UpdateApplyStatus(id, name, namespace string, ch <-chan ev
if err := FormatActionGroupEvent(
e.ActionGroupEvent,
); err != nil {
return err
return nil, nil, err
}
case event.ErrorType:
return e.ErrorEvent.Err
return nil, nil, e.ErrorEvent.Err
case event.ApplyType:
gk := e.ApplyEvent.Identifier.GroupKind
name := e.ApplyEvent.Identifier.Name
Expand Down Expand Up @@ -260,7 +259,18 @@ func (engine *Engine) UpdateApplyStatus(id, name, namespace string, ch <-chan ev
}
}

if err := FormatSummary(namespace, name, statsCollector); err != nil {
return &statsCollector, statusCollector, nil
}

func (engine *Engine) UpdateApplyStatus(id, name, namespace string, ch <-chan event.Event, printStatus bool, vcache map[manifests.GroupName]string) error {
components := []*console.ComponentAttributes{}

statsCollector, statusCollector, err := GetStatusCollector(ch, printStatus)
if err != nil {
return err
}

if err := FormatSummary(namespace, name, *statsCollector); err != nil {
return err
}

Expand Down
Loading

0 comments on commit 26ffaf7

Please sign in to comment.