github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/upgrades/steps124.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package upgrades 5 6 import ( 7 "fmt" 8 "io" 9 "os" 10 "path/filepath" 11 "strings" 12 13 "github.com/juju/juju/state" 14 ) 15 16 // stateStepsFor124 returns upgrade steps for Juju 1.24 that manipulate state directly. 17 func stateStepsFor124() []Step { 18 return []Step{ 19 &upgradeStep{ 20 description: "add block device documents for existing machines", 21 targets: []Target{DatabaseMaster}, 22 run: func(context Context) error { 23 return state.AddDefaultBlockDevicesDocs(context.State()) 24 }}, 25 &upgradeStep{ 26 description: "move service.UnitSeq to sequence collection", 27 targets: []Target{DatabaseMaster}, 28 run: func(context Context) error { 29 return state.MoveServiceUnitSeqToSequence(context.State()) 30 }}, 31 &upgradeStep{ 32 description: "add instance id field to IP addresses", 33 targets: []Target{DatabaseMaster}, 34 run: func(context Context) error { 35 return state.AddInstanceIdFieldOfIPAddresses(context.State()) 36 }}, 37 &upgradeStep{ 38 description: "add UUID field to IP addresses", 39 targets: []Target{DatabaseMaster}, 40 run: func(context Context) error { 41 return state.AddUUIDToIPAddresses(context.State()) 42 }, 43 }, 44 &upgradeStep{ 45 description: "migrate charm archives into environment storage", 46 targets: []Target{DatabaseMaster}, 47 run: func(context Context) error { 48 return migrateCharmStorage(context.State(), context.AgentConfig()) 49 }, 50 }, 51 &upgradeStep{ 52 description: "change entityid field on status history to globalkey", 53 targets: []Target{DatabaseMaster}, 54 run: func(context Context) error { 55 return state.ChangeStatusHistoryEntityId(context.State()) 56 }, 57 }, 58 &upgradeStep{ 59 description: "change updated field on statushistory from time to int", 60 targets: []Target{DatabaseMaster}, 61 run: func(context Context) error { 62 return state.ChangeStatusHistoryUpdatedType(context.State()) 63 }, 64 }, 65 &upgradeStep{ 66 description: "change updated field on status from time to int", 67 targets: []Target{DatabaseMaster}, 68 run: func(context Context) error { 69 return state.ChangeStatusUpdatedType(context.State()) 70 }, 71 }, 72 } 73 } 74 75 // stepsFor124 returns upgrade steps for Juju 1.24 that only need the API. 76 func stepsFor124() []Step { 77 return []Step{ 78 &upgradeStep{ 79 description: "move syslog config from LogDir to DataDir", 80 targets: []Target{AllMachines}, 81 run: moveSyslogConfig, 82 }, 83 } 84 } 85 86 // stateStepsFor1244 returns upgrade steps for Juju 1.24.4 that manipulate state directly. 87 func stateStepsFor1244() []Step { 88 return []Step{ 89 &upgradeStep{ 90 description: "add missing service statuses", 91 targets: []Target{DatabaseMaster}, 92 run: func(context Context) error { 93 return state.AddMissingServiceStatuses(context.State()) 94 }, 95 }, 96 } 97 } 98 99 func moveSyslogConfig(context Context) error { 100 config := context.AgentConfig() 101 logdir := config.LogDir() 102 datadir := config.DataDir() 103 104 // these values were copied from 105 // github.com/juju/juju/utils/syslog/config.go 106 // Yes this is bad, but it is only needed once every for an 107 // upgrade, so it didn't seem worth exporting those values. 108 files := []string{ 109 "ca-cert.pem", 110 "rsyslog-cert.pem", 111 "rsyslog-key.pem", 112 "logrotate.conf", 113 "logrotate.run", 114 } 115 var errs []string 116 for _, f := range files { 117 oldpath := filepath.Join(logdir, f) 118 newpath := filepath.Join(datadir, f) 119 if err := copyFile(newpath, oldpath); err != nil { 120 errs = append(errs, err.Error()) 121 continue 122 } 123 if err := osRemove(oldpath); err != nil { 124 // Don't fail the step if we can't get rid of the old files. 125 // We don't actually care if they still exist or not. 126 logger.Warningf("Can't delete old config file %q: %s", oldpath, err) 127 } 128 } 129 if len(errs) > 0 { 130 return fmt.Errorf("error(s) while moving old syslog config files: %s", strings.Join(errs, "\n")) 131 } 132 return nil 133 } 134 135 // for testing... of course. 136 var osRemove = os.Remove 137 138 // copyFile copies a file from one location to another. It won't overwrite 139 // existing files and will return nil in this case. This is used instead of 140 // os.Rename because os.Rename won't work across partitions. 141 func copyFile(to, from string) error { 142 logger.Debugf("Copying %q to %q", from, to) 143 orig, err := os.Open(from) 144 if os.IsNotExist(err) { 145 logger.Debugf("Old file %q does not exist, skipping.", from) 146 // original doesn't exist, that's fine. 147 return nil 148 } 149 if err != nil { 150 return err 151 } 152 defer orig.Close() 153 info, err := orig.Stat() 154 if err != nil { 155 return err 156 } 157 target, err := os.OpenFile(to, os.O_CREATE|os.O_WRONLY|os.O_EXCL, info.Mode()) 158 if os.IsExist(err) { 159 return nil 160 } 161 defer target.Close() 162 if _, err := io.Copy(target, orig); err != nil { 163 return err 164 } 165 return nil 166 }