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

Make Data Injections Happen asyc w/ Component Deployment #425

Merged
merged 25 commits into from
Mar 31, 2022
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
6f28dcd
Update data-injection demo
RothAndrew Mar 29, 2022
7726485
move data -> components.dataInjection
jeff-mccoy Mar 29, 2022
3b72f46
make data injection async
YrrepNoj Mar 29, 2022
cf3c538
add waitgroup for async data injection
YrrepNoj Mar 29, 2022
0a56346
Merge remote-tracking branch 'origin/feature/add-init-container-to-da…
jeff-mccoy Mar 30, 2022
e37041e
Merge branch 'master' into fix/data-injection-race-condition
jeff-mccoy Mar 30, 2022
b128056
add containers to zarf data injection
jeff-mccoy Mar 30, 2022
c855a49
Merge branch 'master' into fix/data-injection-race-condition
jeff-mccoy Mar 30, 2022
1c741f2
fixing jon & andy's bugs 🤣👀
jeff-mccoy Mar 30, 2022
caea283
Merge remote-tracking branch 'origin/fix/data-injection-race-conditio…
jeff-mccoy Mar 30, 2022
ba01450
get exact name of data-injection test pod
YrrepNoj Mar 30, 2022
a48c9f0
fix typo and remove unnecessary retry loop
YrrepNoj Mar 30, 2022
3ddc741
update zarf schema
YrrepNoj Mar 30, 2022
9b73df8
add loop check for data injected after pod comes up
YrrepNoj Mar 30, 2022
f719957
increase retry timeout
YrrepNoj Mar 30, 2022
204260b
data injection test checks for something that's actually there
YrrepNoj Mar 30, 2022
09e0882
remove pvc from data-injection example
YrrepNoj Mar 30, 2022
9325408
add readinessProbe for sync complete
YrrepNoj Mar 30, 2022
4f0c628
try a little tenderness
jeff-mccoy Mar 30, 2022
585d251
Merge remote-tracking branch 'origin/fix/data-injection-race-conditio…
jeff-mccoy Mar 30, 2022
c646fb4
check for pod status during data-injection test
YrrepNoj Mar 30, 2022
8809985
stupid sleep
YrrepNoj Mar 30, 2022
4079af5
code that proves k8s api is weird if you run it enough
YrrepNoj Mar 31, 2022
66b894f
remove debugging logs
YrrepNoj Mar 31, 2022
c4d3f0a
add back cleanup defer statement
YrrepNoj Mar 31, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions cli/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,6 @@ func GetPackageName() string {
return fmt.Sprintf("%s-%s-%s.%s", prefix, metadata.Name, GetArch(), suffix)
}

func GetDataInjections() []types.ZarfData {
return active.Data
}

func GetMetaData() types.ZarfMetadata {
return active.Metadata
}
Expand Down
28 changes: 14 additions & 14 deletions cli/internal/packager/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,20 @@ import (
)

type componentPaths struct {
base string
files string
charts string
values string
repos string
manifests string
base string
files string
charts string
values string
repos string
manifests string
dataInjections string
}
type tempPaths struct {
base string
injectZarfBinary string
injectBinary string
seedImage string
images string
dataInjections string
components string
}

Expand All @@ -49,7 +49,6 @@ func createPaths() tempPaths {
injectBinary: basePath + "/zarf-injector",
seedImage: basePath + "/seed-image.tar",
images: basePath + "/images.tar",
dataInjections: basePath + "/data",
components: basePath + "/components",
}
}
Expand All @@ -63,12 +62,13 @@ func createComponentPaths(basePath string, component types.ZarfComponent) compon
basePath = basePath + "/" + component.Name
_ = utils.CreateDirectory(basePath, 0700)
return componentPaths{
base: basePath,
files: basePath + "/files",
charts: basePath + "/charts",
repos: basePath + "/repos",
manifests: basePath + "/manifests",
values: basePath + "/values",
base: basePath,
files: basePath + "/files",
charts: basePath + "/charts",
repos: basePath + "/repos",
manifests: basePath + "/manifests",
dataInjections: basePath + "/data",
values: basePath + "/values",
}
}

Expand Down
21 changes: 12 additions & 9 deletions cli/internal/packager/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ func Create() {

packageName := config.GetPackageName()
components := GetComposedComponents()
dataInjections := config.GetDataInjections()
seedImage := config.GetSeedImage()

configFile := tempPath.base + "/zarf.yaml"
Expand Down Expand Up @@ -71,15 +70,8 @@ func Create() {
// Include the injection things we need, note that zarf-registry must be created by `make build-injector` first
utils.CreatePathAndCopy("injector/zarf-registry", tempPath.injectZarfBinary)
utils.CreatePathAndCopy("injector/zarf-injector", tempPath.injectBinary)
} else {
// Init packages do not use data or utilityCluster keys
if len(dataInjections) > 0 {
for _, data := range dataInjections {
destinationFile := tempPath.dataInjections + "/" + filepath.Base(data.Target.Path)
utils.CreatePathAndCopy(data.Source, destinationFile)
}
}
}

_ = os.RemoveAll(packageName)
err := archiver.Archive([]string{tempPath.base + "/"}, packageName)
if err != nil {
Expand Down Expand Up @@ -135,6 +127,17 @@ func addComponent(tempPath tempPaths, component types.ZarfComponent) {
}
}

if len(component.DataInjections) > 0 {
spinner := message.NewProgressSpinner("Loading data injections")
defer spinner.Stop()
for _, data := range component.DataInjections {
spinner.Updatef("Copying data injection %s for %s", data.Target.Path, data.Target.Selector)
destinationFile := componentPath.dataInjections + "/" + filepath.Base(data.Target.Path)
utils.CreatePathAndCopy(data.Source, destinationFile)
}
spinner.Success()
}

if len(component.Manifests) > 0 {
spinner := message.NewProgressSpinner("Loading %d manifests", len(component.Manifests))
defer spinner.Stop()
Expand Down
130 changes: 80 additions & 50 deletions cli/internal/packager/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"path/filepath"
"strconv"
"strings"
"sync"
"time"

"github.com/defenseunicorns/zarf/cli/types"

Expand Down Expand Up @@ -81,41 +83,28 @@ func Deploy() {
deployComponents(tempPath, component)
}

if config.IsZarfInitConfig() {
// If this is the end of an initconfig, cleanup and tell the user we're ready to roll
_ = os.Remove(".zarf-registry")
if len(connectStrings) > 0 {
list := pterm.TableData{{" Connect Command", "Description"}}
// Loop over each connecStrings and convert to pterm.TableData
for name, connect := range connectStrings {
name = fmt.Sprintf(" zarf connect %s", name)
list = append(list, []string{name, connect.Description})
}

// Create the table output with the data
_ = pterm.DefaultTable.WithHasHeader().WithData(list).Render()
}

pterm.Success.Println("Zarf deployment complete")
pterm.Println()
pterm.Success.Println("Zarf deployment complete")
pterm.Println()

if config.IsZarfInitConfig() {
_ = pterm.DefaultTable.WithHasHeader().WithData(pterm.TableData{
{" Application", "Username", "Password", "Connect"},
{" Logging", "zarf-admin", config.GetSecret(config.StateLogging), "zarf connect logging"},
{" Git", config.ZarfGitPushUser, config.GetSecret(config.StateGitPush), "zarf connect git"},
{" Registry", "zarf-push-user", config.GetSecret(config.StateRegistryPush), "zarf connect registry"},
}).Render()
} else {
// Otherwise, look for any datainjections to run after the components
dataInjectionList := config.GetDataInjections()
if len(dataInjectionList) > 0 {
message.Info("Loading data injections")
handleDataInjection(dataInjectionList, tempPath)
}

pterm.Success.Println("Zarf deployment complete")
pterm.Println()

if len(connectStrings) > 0 {
list := pterm.TableData{{" Connect Command", "Description"}}
// Loop over each connecStrings and convert to pterm.TableData
for name, connect := range connectStrings {
name = fmt.Sprintf(" zarf connect %s", name)
list = append(list, []string{name, connect.Description})
}

// Create the table output with the data
_ = pterm.DefaultTable.WithHasHeader().WithData(list).Render()
}
}

// All done
Expand Down Expand Up @@ -179,6 +168,18 @@ func deployComponents(tempPath tempPaths, component types.ZarfComponent) {
spinner.Success()
}

// Start any data injection async
if len(component.DataInjections) > 0 {
var waitGroup sync.WaitGroup

message.Info("Loading data injections")
for _, data := range component.DataInjections {
waitGroup.Add(1)
go handleDataInjection(&waitGroup, data, componentPath)
}
defer waitGroup.Wait()
}

if isSeedRegistry {
preSeedRegistry(tempPath)
valueTemplate = template.Generate()
Expand Down Expand Up @@ -261,44 +262,73 @@ func deployComponents(tempPath tempPaths, component types.ZarfComponent) {
}
}

// handleDataInjection performs data-copy operations into a pod
// Wait for the target pod(s) to come up and inject the data into them
// todo: this currently requires kubectl but we should have enough k8s work to make this native now
func handleDataInjection(dataInjectionList []types.ZarfData, tempPath tempPaths) {
injectionCompletionMarker := tempPath.dataInjections + "/.zarf-sync-complete"
func handleDataInjection(wg *sync.WaitGroup, data types.ZarfDataInjection, componentPath componentPaths) {
defer wg.Done()

injectionCompletionMarker := componentPath.dataInjections + "/.zarf-sync-complete"
if err := utils.WriteFile(injectionCompletionMarker, []byte("🦄")); err != nil {
return
}
for _, data := range dataInjectionList {
sourceFile := tempPath.dataInjections + "/" + filepath.Base(data.Target.Path)
pods := k8s.WaitForPodsAndContainers(data.Target, true)

for _, pod := range pods {
timeout := time.After(15 * time.Minute)
for {
// delay check 2 seconds
time.Sleep(2 * time.Second)
select {

// on timeout abort
case <-timeout:
message.Warnf("data injection into target %v timed out\n", data.Target.Namespace)
return

default:
sourceFile := componentPath.dataInjections + "/" + filepath.Base(data.Target.Path)

// Wait until the pod we are injecting data into becomes available
pods := k8s.WaitForPodsAndContainers(data.Target, true)
if len(pods) < 1 {
continue
}

// Define injection destination
destination := data.Target.Path
if destination == "/"+filepath.Base(destination) {
// Handle top-level directory targets
destination = "/"
}
cpPodExecArgs := []string{"-n", data.Target.Namespace, "cp", sourceFile, pod + ":" + destination}

if data.Target.Container != "" {
// Append the container args if they are specified
cpPodExecArgs = append(cpPodExecArgs, "-c", data.Target.Container)
}
// Inject into all the pods
for _, pod := range pods {
cpPodExecArgs := []string{"-n", data.Target.Namespace, "cp", sourceFile, pod + ":" + destination}

_, err := utils.ExecCommand(true, nil, "kubectl", cpPodExecArgs...)
if err != nil {
message.Warn("Error copying data into the pod")
} else {
// Leave a marker in the target container for pods to track the sync action
cpPodExecArgs[3] = injectionCompletionMarker
cpPodExecArgs[4] = pod + ":" + data.Target.Path
_, err = utils.ExecCommand(true, nil, "kubectl", cpPodExecArgs...)
if data.Target.Container != "" {
// Append the container args if they are specified
cpPodExecArgs = append(cpPodExecArgs, "-c", data.Target.Container)
}

// Do the actual data injection
_, err := utils.ExecCommand(true, nil, "kubectl", cpPodExecArgs...)
if err != nil {
message.Warn("Error saving the zarf sync completion file")
message.Warnf("Error copying data into the pod %v: %v\n", pod, err)
continue
} else {
// Leave a marker in the target container for pods to track the sync action
cpPodExecArgs[3] = injectionCompletionMarker
cpPodExecArgs[4] = pod + ":" + data.Target.Path
_, err = utils.ExecCommand(true, nil, "kubectl", cpPodExecArgs...)
if err != nil {
message.Warnf("Error saving the zarf sync completion file after injection into pod %v\n", pod)
}
}
}

// Cleanup now to reduce disk pressure
_ = os.RemoveAll(sourceFile)

// Return to stop the loop
return
}
// Cleanup now to reduce disk pressure
_ = os.RemoveAll(sourceFile)
}
}
8 changes: 5 additions & 3 deletions cli/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ type ZarfComponent struct {
// Repos are any git repos that need to be pushed into the gitea server
Repos []string `yaml:"repos,omitempty"`

// Data pacakges to push into a running cluster
DataInjections []ZarfDataInjection `yaml:"dataInjections,omitempty"`

// Scripts are custom commands that run before or after package deployment
Scripts ZarfComponentScripts `yaml:"scripts,omitempty"`

Expand Down Expand Up @@ -92,8 +95,8 @@ type ZarfContainerTarget struct {
Path string `yaml:"path"`
}

// ZarfData is a data-injection definition
type ZarfData struct {
// ZarfDataInjection is a data-injection definition
type ZarfDataInjection struct {
Source string `yaml:"source"`
Target ZarfContainerTarget `yaml:"target"`
}
Expand All @@ -112,7 +115,6 @@ type ZarfPackage struct {
Kind string `yaml:"kind,omitempty"`
Metadata ZarfMetadata `yaml:"metadata,omitempty"`
Build ZarfBuildData `yaml:"build,omitempty"`
Data []ZarfData `yaml:"data,omitempty"`
Components []ZarfComponent `yaml:"components,omitempty"`
Seed string `yaml:"seed,omitempty"`
}
Expand Down
24 changes: 0 additions & 24 deletions examples/data-injection/manifests/data-injection.yaml

This file was deleted.

Loading