github.com/replicatedhq/ship@v0.55.0/pkg/lifecycle/daemon/headless/daemon.go (about) 1 package headless 2 3 import ( 4 "context" 5 "path" 6 "strings" 7 8 "github.com/go-kit/kit/log" 9 "github.com/go-kit/kit/log/level" 10 "github.com/mitchellh/cli" 11 "github.com/pkg/errors" 12 "github.com/replicatedhq/ship/pkg/api" 13 "github.com/replicatedhq/ship/pkg/constants" 14 "github.com/replicatedhq/ship/pkg/lifecycle/daemon/daemontypes" 15 "github.com/replicatedhq/ship/pkg/lifecycle/render/config/resolve" 16 "github.com/replicatedhq/ship/pkg/state" 17 "github.com/spf13/afero" 18 "github.com/spf13/viper" 19 ) 20 21 var _ daemontypes.Daemon = &HeadlessDaemon{} 22 23 type HeadlessDaemon struct { 24 StateManager state.Manager 25 Logger log.Logger 26 UI cli.Ui 27 ConfigRenderer *resolve.APIConfigRenderer 28 FS afero.Afero 29 ResolvedConfig map[string]interface{} 30 31 YesApplyTerraform bool 32 } 33 34 func (d *HeadlessDaemon) AwaitShutdown() error { 35 return nil 36 } 37 38 func NewHeadlessDaemon( 39 ui cli.Ui, 40 logger log.Logger, 41 renderer *resolve.APIConfigRenderer, 42 stateManager state.Manager, 43 fs afero.Afero, 44 v *viper.Viper, 45 ) daemontypes.Daemon { 46 return &HeadlessDaemon{ 47 StateManager: stateManager, 48 Logger: logger, 49 UI: ui, 50 ConfigRenderer: renderer, 51 FS: fs, 52 YesApplyTerraform: v.GetBool("terraform-apply-yes"), 53 } 54 } 55 56 func (d *HeadlessDaemon) PushKustomizeStep(context.Context, daemontypes.Kustomize) {} 57 func (d *HeadlessDaemon) PushMessageStep(context.Context, daemontypes.Message, []daemontypes.Action) {} 58 func (d *HeadlessDaemon) PushRenderStep(context.Context, daemontypes.Render) {} 59 60 func (d *HeadlessDaemon) KustomizeSavedChan() chan interface{} { 61 ch := make(chan interface{}, 1) 62 level.Debug(d.Logger).Log("event", "kustomize.skip", "detail", "running in automation, not waiting for kustomize") 63 ch <- nil 64 return ch 65 } 66 67 func (d *HeadlessDaemon) UnforkSavedChan() chan interface{} { 68 ch := make(chan interface{}, 1) 69 ch <- nil 70 return ch 71 } 72 73 func (d *HeadlessDaemon) PushHelmIntroStep(context.Context, daemontypes.HelmIntro, []daemontypes.Action) { 74 } 75 76 func (d *HeadlessDaemon) PushHelmValuesStep(ctx context.Context, helmValues daemontypes.HelmValues, actions []daemontypes.Action) { 77 warn := level.Warn(log.With(d.Logger, "struct", "HeadlessDaemon", "method", "PushHelmValuesStep")) 78 79 defaultValues := helmValues.DefaultValues 80 if defaultValues == "" { 81 v, err := d.FS.ReadFile(path.Join(constants.HelmChartPath, "values.yaml")) 82 if err != nil { 83 warn.Log("event", "push helm values fail while reading defaults", "err", err) 84 } else { 85 defaultValues = string(v) 86 } 87 } 88 89 if err := d.HeadlessSaveHelmValues(ctx, helmValues.Values, defaultValues); err != nil { 90 warn.Log("event", "push helm values step fail", "err", err) 91 } 92 } 93 94 func (d *HeadlessDaemon) HeadlessSaveHelmValues(ctx context.Context, helmValues, defaultValues string) error { 95 warn := level.Warn(log.With(d.Logger, "struct", "HeadlessDaemon", "method", "HeadlessSaveHelmValues")) 96 err := d.StateManager.SerializeHelmValues(helmValues, defaultValues) 97 if err != nil { 98 warn.Log("event", "headless save helm values fail", "err", err) 99 return errors.Wrap(err, "write new values") 100 } 101 102 return nil 103 } 104 105 func (d *HeadlessDaemon) PushStreamStep(context.Context, <-chan daemontypes.Message) {} 106 107 func (d *HeadlessDaemon) CleanPreviousStep() {} 108 109 func (d *HeadlessDaemon) TerraformConfirmedChan() chan bool { 110 ch := make(chan bool, 1) 111 112 if !d.YesApplyTerraform { 113 level.Info(d.Logger).Log("event", "terraform.skip", "detail", "skipping running terraform because --terraform-apply-yes was not set") 114 ch <- false 115 return ch 116 } 117 118 level.Info(d.Logger).Log("event", "terraform.apply", "detail", "running terraform because --terraform-apply-yes was set") 119 ch <- true 120 return ch 121 } 122 123 func (d *HeadlessDaemon) EnsureStarted(ctx context.Context, release *api.Release) chan error { 124 warn := level.Warn(log.With(d.Logger, "struct", "fakeDaemon", "method", "EnsureStarted")) 125 126 chanerrors := make(chan error) 127 128 if err := d.HeadlessResolve(ctx, release); err != nil { 129 warn.Log("event", "headless resolved failed", "err", err) 130 go func() { 131 chanerrors <- err 132 close(chanerrors) 133 }() 134 } 135 136 return chanerrors 137 } 138 139 func (d *HeadlessDaemon) SetStepName(context.Context, string) {} 140 141 func (d *HeadlessDaemon) AllStepsDone(context.Context) {} 142 143 func (d *HeadlessDaemon) MessageConfirmedChan() chan string { 144 ch := make(chan string) 145 close(ch) 146 return ch 147 } 148 149 func (d *HeadlessDaemon) ConfigSavedChan() chan interface{} { 150 ch := make(chan interface{}) 151 close(ch) 152 return ch 153 } 154 155 func (d *HeadlessDaemon) GetCurrentConfig() (map[string]interface{}, error) { 156 if d.ResolvedConfig != nil { 157 return d.ResolvedConfig, nil 158 } 159 160 warn := level.Warn(log.With(d.Logger, "struct", "fakeDaemon", "method", "getCurrentConfig")) 161 currentConfig, err := d.StateManager.CachedState() 162 if err != nil { 163 warn.Log("event", "state missing", "err", err) 164 return nil, err 165 } 166 167 config, err := currentConfig.CurrentConfig() 168 if err != nil { 169 warn.Log("event", "get config", "err", err) 170 return nil, err 171 } 172 173 return config, nil 174 } 175 176 func (d *HeadlessDaemon) HeadlessResolve(ctx context.Context, release *api.Release) error { 177 warn := level.Warn(log.With(d.Logger, "struct", "fakeDaemon", "method", "HeadlessResolve")) 178 currentConfig, err := d.GetCurrentConfig() 179 if err != nil { 180 warn.Log("event", "get config failed", "err", err) 181 return err 182 } 183 184 resolved, err := d.ConfigRenderer.ResolveConfig(ctx, release, currentConfig, make(map[string]interface{}), false) 185 if err != nil { 186 warn.Log("event", "resolveconfig failed", "err", err) 187 return err 188 } 189 190 if validateState := resolve.ValidateConfig(resolved); validateState != nil { 191 var invalidItemNames []string 192 for _, invalidConfigItems := range validateState { 193 invalidItemNames = append(invalidItemNames, invalidConfigItems.Name) 194 } 195 196 err := errors.Errorf( 197 "validate config failed. missing config values: %s", 198 strings.Join(invalidItemNames, ","), 199 ) 200 warn.Log("event", "state invalid", "err", err) 201 return err 202 } 203 204 templateContext := make(map[string]interface{}) 205 for _, configGroup := range resolved { 206 for _, configItem := range configGroup.Items { 207 templateContext[configItem.Name] = configItem.Value 208 } 209 } 210 211 d.ResolvedConfig = templateContext 212 return nil 213 } 214 215 func (d *HeadlessDaemon) SetProgress(progress daemontypes.Progress) { 216 if progress.Type == "string" { 217 d.UI.Output(progress.Detail) 218 } 219 } 220 221 func (d *HeadlessDaemon) ClearProgress() {}