Skip to content

Commit 5d3854a

Browse files
Merge pull request #328 from yfrickx-kbc/feature/allow-in-place-upgrade
closes #327: Add upgrade command for in-place-upgrade
2 parents ce7ea5a + 8a1fd63 commit 5d3854a

File tree

4 files changed

+144
-42
lines changed

4 files changed

+144
-42
lines changed

cmd/aem/instance.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ func (c *CLI) instanceCmd() *cobra.Command {
2424
cmd.AddCommand(c.instanceAwaitCmd())
2525
cmd.AddCommand(c.instanceBackupCmd())
2626
cmd.AddCommand(c.instanceImportCmd())
27+
cmd.AddCommand(c.instanceUpgradeCmd())
2728
return cmd
2829
}
2930

@@ -142,6 +143,32 @@ func (c *CLI) instanceCreateCmd() *cobra.Command {
142143
}
143144
}
144145

146+
func (c *CLI) instanceUpgradeCmd() *cobra.Command {
147+
return &cobra.Command{
148+
Use: "upgrade",
149+
Short: "Upgrades AEM instance(s) if needed",
150+
Aliases: []string{"update"},
151+
Run: func(cmd *cobra.Command, args []string) {
152+
localInstances, err := c.aem.InstanceManager().SomeLocals()
153+
if err != nil {
154+
c.Error(err)
155+
return
156+
}
157+
upgradedInstances, err := c.aem.InstanceManager().Upgrade(localInstances)
158+
if err != nil {
159+
c.Error(err)
160+
return
161+
}
162+
c.SetOutput("upgraded", upgradedInstances)
163+
if len(upgradedInstances) > 0 {
164+
c.Changed(fmt.Sprintf("upgraded instance(s) (%d)", len(upgradedInstances)))
165+
} else {
166+
c.Ok("no instance(s) to upgrade")
167+
}
168+
},
169+
}
170+
}
171+
145172
func (c *CLI) instanceStartCmd() *cobra.Command {
146173
return &cobra.Command{
147174
Use: "start",

pkg/instance_manager.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ func (im *InstanceManager) newFromConfig(id string) *Instance {
159159
i.local.SecretVars = cv.GetStringSlice(fmt.Sprintf("instance.config.%s.secret_vars", id))
160160
i.local.SlingProps = cv.GetStringSlice(fmt.Sprintf("instance.config.%s.sling_props", id))
161161
i.local.UnpackDir = cv.GetString(fmt.Sprintf("instance.config.%s.unpack_dir", id))
162+
i.local.AllowInPlaceUpgrade = cv.GetBool(fmt.Sprintf("instance.config.%s.allow_in_place_upgrade", id))
162163
}
163164
return i
164165
}

pkg/local_instance.go

Lines changed: 91 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,15 @@ import (
2929
type LocalInstance struct {
3030
instance *Instance
3131

32-
Version string
33-
JvmOpts []string
34-
StartOpts []string
35-
RunModes []string
36-
EnvVars []string
37-
SecretVars []string
38-
SlingProps []string
39-
UnpackDir string
32+
Version string
33+
JvmOpts []string
34+
StartOpts []string
35+
RunModes []string
36+
EnvVars []string
37+
SecretVars []string
38+
SlingProps []string
39+
UnpackDir string
40+
AllowInPlaceUpgrade bool
4041
}
4142

4243
type LocalInstanceState struct {
@@ -50,15 +51,16 @@ type LocalInstanceState struct {
5051
}
5152

5253
const (
53-
LocalInstanceScriptStart = "start"
54-
LocalInstanceScriptStop = "stop"
55-
LocalInstanceScriptStatus = "status"
56-
LocalInstanceBackupExtension = "aemb.tar.zst"
57-
LocalInstanceUser = "admin"
58-
LocalInstanceWorkDirName = common.AppId
59-
LocalInstanceNameCommon = "common"
60-
LocalInstanceSecretsDir = "conf/secret"
61-
LocalInstanceVersionDefault = "1"
54+
LocalInstanceScriptStart = "start"
55+
LocalInstanceScriptStop = "stop"
56+
LocalInstanceScriptStatus = "status"
57+
LocalInstanceBackupExtension = "aemb.tar.zst"
58+
LocalInstanceUser = "admin"
59+
LocalInstanceWorkDirName = common.AppId
60+
LocalInstanceNameCommon = "common"
61+
LocalInstanceSecretsDir = "conf/secret"
62+
LocalInstanceVersionDefault = "1"
63+
LocalInstanceAllowInPlaceUpgradeDefault = false
6264
)
6365

6466
func (li LocalInstance) Instance() *Instance {
@@ -74,6 +76,7 @@ func NewLocal(i *Instance) *LocalInstance {
7476
li.EnvVars = []string{}
7577
li.SecretVars = []string{}
7678
li.SlingProps = []string{}
79+
li.AllowInPlaceUpgrade = LocalInstanceAllowInPlaceUpgradeDefault
7780
return li
7881
}
7982

@@ -185,18 +188,21 @@ var (
185188
LocalInstancePasswordRegex = regexp.MustCompile("^[a-zA-Z0-9_]{5,}$")
186189
)
187190

188-
func (li LocalInstance) CheckRecreationNeeded() error {
191+
func (li LocalInstance) CheckUpgradeNeeded() (bool, error) {
189192
createLock := li.createLock()
190193
if createLock.IsLocked() {
191194
state, err := createLock.State()
192195
if err != nil {
193-
return err
196+
return false, err
194197
}
195198
if !state.UpToDate {
196-
return fmt.Errorf("%s > outdated and need to be recreated as distribution JAR changed from '%s' to '%s'", li.instance.IDColor(), state.Locked.JarName, state.Current.JarName)
199+
if li.AllowInPlaceUpgrade {
200+
return true, nil
201+
}
202+
return true, fmt.Errorf("%s > outdated and need to be upgraded as distribution JAR changed from '%s' to '%s'", li.instance.IDColor(), state.Locked.JarName, state.Current.JarName)
197203
}
198204
}
199-
return nil
205+
return false, nil
200206
}
201207

202208
func (li LocalInstance) CheckPassword() error {
@@ -241,6 +247,21 @@ func (li LocalInstance) Create() error {
241247
return nil
242248
}
243249

250+
func (li LocalInstance) Upgrade() error {
251+
log.Infof("%s > upgrading", li.instance.IDColor())
252+
if err := li.unpackJarFile(); err != nil {
253+
return err
254+
}
255+
if err := li.copyLicenseFile(); err != nil {
256+
return err
257+
}
258+
if err := li.adapt(); err != nil {
259+
return err
260+
}
261+
log.Infof("%s > upgraded", li.instance.IDColor())
262+
return nil
263+
}
264+
244265
func (li LocalInstance) Import() error {
245266
log.Infof("%s > importing", li.instance.IDColor())
246267

@@ -294,6 +315,33 @@ func (li LocalInstance) unpackJarFile() error {
294315
if !pathx.Exists(startScript) {
295316
return fmt.Errorf("%s > unpacking files went wrong as e.g start script does not exist '%s'", li.instance.IDColor(), startScript)
296317
}
318+
319+
// Only check crx-quickstart/app for JARs
320+
appDir := filepath.Join(li.QuickstartDir(), "app")
321+
jarFiles, err := filepath.Glob(filepath.Join(appDir, "*.jar"))
322+
if err != nil {
323+
return fmt.Errorf("%s > error searching for JAR files in app dir: %w", li.instance.IDColor(), err)
324+
}
325+
if len(jarFiles) > 1 {
326+
type jarInfo struct {
327+
path string
328+
modTime time.Time
329+
}
330+
var jars []jarInfo
331+
for _, path := range jarFiles {
332+
info, err := os.Stat(path)
333+
if err == nil {
334+
jars = append(jars, jarInfo{path, info.ModTime()})
335+
}
336+
}
337+
sort.Slice(jars, func(i, j int) bool {
338+
return jars[i].modTime.After(jars[j].modTime)
339+
})
340+
for _, ji := range jars[1:] {
341+
log.Infof("%s > removing outdated JAR from app dir: %s", li.instance.IDColor(), ji.path)
342+
_ = os.Remove(ji.path)
343+
}
344+
}
297345
return nil
298346
}
299347

@@ -538,31 +586,33 @@ func (li LocalInstance) updateLock() osx.Lock[localInstanceUpdateLock] {
538586
return zero, err
539587
}
540588
return localInstanceUpdateLock{
541-
Version: li.Version,
542-
HTTPPort: li.instance.HTTP().Port(),
543-
RunModes: strings.Join(li.RunModes, ","),
544-
JVMOpts: strings.Join(li.JvmOpts, " "),
545-
JavaHome: javaHomeDir,
546-
Password: cryptox.HashString(li.instance.password),
547-
EnvVars: strings.Join(li.EnvVars, ","),
548-
SecretVars: cryptox.HashString(strings.Join(li.SecretVars, ",")),
549-
SlingProps: strings.Join(li.SlingProps, ","),
550-
Overrides: overrides,
589+
Version: li.Version,
590+
HTTPPort: li.instance.HTTP().Port(),
591+
RunModes: strings.Join(li.RunModes, ","),
592+
JVMOpts: strings.Join(li.JvmOpts, " "),
593+
JavaHome: javaHomeDir,
594+
Password: cryptox.HashString(li.instance.password),
595+
EnvVars: strings.Join(li.EnvVars, ","),
596+
SecretVars: cryptox.HashString(strings.Join(li.SecretVars, ",")),
597+
SlingProps: strings.Join(li.SlingProps, ","),
598+
Overrides: overrides,
599+
AllowInPlaceUpgrade: li.AllowInPlaceUpgrade,
551600
}, nil
552601
})
553602
}
554603

555604
type localInstanceUpdateLock struct {
556-
Version string `yaml:"version"`
557-
JVMOpts string `yaml:"jvm_opts"`
558-
JavaHome string `yaml:"java_home"`
559-
RunModes string `yaml:"run_modes"`
560-
HTTPPort string `yaml:"http_port"`
561-
Password string `yaml:"password"`
562-
Overrides string `yaml:"overrides"`
563-
EnvVars string `yaml:"env_vars"`
564-
SecretVars string `yaml:"secret_vars"`
565-
SlingProps string `yaml:"sling_props"`
605+
Version string `yaml:"version"`
606+
JVMOpts string `yaml:"jvm_opts"`
607+
JavaHome string `yaml:"java_home"`
608+
RunModes string `yaml:"run_modes"`
609+
HTTPPort string `yaml:"http_port"`
610+
Password string `yaml:"password"`
611+
Overrides string `yaml:"overrides"`
612+
EnvVars string `yaml:"env_vars"`
613+
SecretVars string `yaml:"secret_vars"`
614+
SlingProps string `yaml:"sling_props"`
615+
AllowInPlaceUpgrade bool `yaml:"allow_in_place_upgrade"`
566616
}
567617

568618
func (li LocalInstance) Stop() error {

pkg/local_instance_manager.go

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ func (o *LocalOpts) Initialize() error {
7272
}
7373
// post-validation phase
7474
for _, instance := range o.manager.Locals() {
75-
if err := instance.Local().CheckRecreationNeeded(); err != nil { // depends on SDK prepare
75+
if _, err := instance.Local().CheckUpgradeNeeded(); err != nil { // depends on SDK prepare
7676
return err
7777
}
7878
}
@@ -129,6 +129,10 @@ func (im *InstanceManager) CreateAll() ([]Instance, error) {
129129
return im.Create(im.Locals())
130130
}
131131

132+
func (im *InstanceManager) UpgradeAll() ([]Instance, error) {
133+
return im.Upgrade(im.Locals())
134+
}
135+
132136
func (im *InstanceManager) Create(instances []Instance) ([]Instance, error) {
133137
created := []Instance{}
134138
if err := im.LocalOpts.Initialize(); err != nil {
@@ -147,6 +151,26 @@ func (im *InstanceManager) Create(instances []Instance) ([]Instance, error) {
147151
return created, nil
148152
}
149153

154+
func (im *InstanceManager) Upgrade(instances []Instance) ([]Instance, error) {
155+
upgraded := []Instance{}
156+
if err := im.LocalOpts.Initialize(); err != nil {
157+
return upgraded, err
158+
}
159+
log.Info(InstancesMsg(instances, "upgrading"))
160+
for _, i := range instances {
161+
if !i.local.IsCreated() {
162+
return nil, fmt.Errorf("instance not yet created: %s", i.IDColor())
163+
} else if upgradeNeeded, _ := i.local.CheckUpgradeNeeded(); upgradeNeeded {
164+
err := i.local.Upgrade()
165+
if err != nil {
166+
return nil, err
167+
}
168+
upgraded = append(upgraded, i)
169+
}
170+
}
171+
return upgraded, nil
172+
}
173+
150174
func (im *InstanceManager) Import(instances []Instance) ([]Instance, error) {
151175
imported := []Instance{}
152176
log.Info(InstancesMsg(instances, "importing"))

0 commit comments

Comments
 (0)