157
187
resolveError{state.ResolvedNoHooks},
159
status: params.StatusActive,
161
waitHooks{"config-changed", "start"},
189
status: params.StatusIdle,
192
statusGetter: unitStatusGetter,
193
status: params.StatusUnknown,
195
waitHooks{"leader-elected", "config-changed", "start"},
163
197
"install hook fail and retry",
164
198
startupError{"install"},
167
201
resolveError{state.ResolvedRetryHooks},
169
status: params.StatusError,
170
info: `hook failed: "install"`,
203
statusGetter: unitStatusGetter,
204
status: params.StatusError,
205
info: `hook failed: "install"`,
171
206
data: map[string]interface{}{
172
207
"hook": "install",
229
270
s.runUniterTests(c, []uniterTest{
231
272
"resolved is cleared before moving on to next hook",
232
createCharm{badHooks: []string{"install", "config-changed", "start"}},
273
createCharm{badHooks: []string{"install", "leader-elected", "config-changed", "start"}},
236
status: params.StatusError,
237
info: `hook failed: "install"`,
277
statusGetter: unitStatusGetter,
278
status: params.StatusError,
279
info: `hook failed: "install"`,
238
280
data: map[string]interface{}{
239
281
"hook": "install",
242
284
resolveError{state.ResolvedNoHooks},
244
status: params.StatusError,
245
info: `hook failed: "config-changed"`,
286
statusGetter: unitStatusGetter,
287
status: params.StatusError,
288
info: `hook failed: "leader-elected"`,
289
data: map[string]interface{}{
290
"hook": "leader-elected",
293
resolveError{state.ResolvedNoHooks},
295
statusGetter: unitStatusGetter,
296
status: params.StatusError,
297
info: `hook failed: "config-changed"`,
246
298
data: map[string]interface{}{
247
299
"hook": "config-changed",
250
302
resolveError{state.ResolvedNoHooks},
252
status: params.StatusError,
253
info: `hook failed: "start"`,
304
statusGetter: unitStatusGetter,
305
status: params.StatusError,
306
info: `hook failed: "start"`,
254
307
data: map[string]interface{}{
965
func (s *UniterSuite) TestRunCommand(c *gc.C) {
967
testFile := func(name string) string {
968
return filepath.Join(testDir, name)
970
echoUnitNameToFile := func(name string) string {
971
path := filepath.Join(testDir, name)
972
template := "echo juju run ${JUJU_UNIT_NAME} > %s.tmp; mv %s.tmp %s"
973
return fmt.Sprintf(template, path, path, path)
975
adminTag := s.AdminUserTag(c)
977
s.runUniterTests(c, []uniterTest{
979
"run commands: environment",
981
runCommands{echoUnitNameToFile("run.output")},
982
verifyFile{filepath.Join(testDir, "run.output"), "juju run u/0\n"},
984
"run commands: jujuc commands",
985
quickStartRelation{},
987
fmt.Sprintf("owner-get tag > %s", testFile("jujuc.output")),
988
fmt.Sprintf("unit-get private-address >> %s", testFile("jujuc.output")),
989
fmt.Sprintf("unit-get public-address >> %s", testFile("jujuc.output")),
992
testFile("jujuc.output"),
993
adminTag.String() + "\nprivate.address.example.com\npublic.address.example.com\n",
996
"run commands: jujuc environment",
997
quickStartRelation{},
999
fmt.Sprintf("echo $JUJU_RELATION_ID > %s", testFile("jujuc-env.output")),
1000
fmt.Sprintf("echo $JUJU_REMOTE_UNIT >> %s", testFile("jujuc-env.output")),
1003
testFile("jujuc-env.output"),
1007
"run commands: proxy settings set",
1008
quickStartRelation{},
1009
setProxySettings{Http: "http", Https: "https", Ftp: "ftp", NoProxy: "localhost"},
1011
fmt.Sprintf("echo $http_proxy > %s", testFile("proxy.output")),
1012
fmt.Sprintf("echo $HTTP_PROXY >> %s", testFile("proxy.output")),
1013
fmt.Sprintf("echo $https_proxy >> %s", testFile("proxy.output")),
1014
fmt.Sprintf("echo $HTTPS_PROXY >> %s", testFile("proxy.output")),
1015
fmt.Sprintf("echo $ftp_proxy >> %s", testFile("proxy.output")),
1016
fmt.Sprintf("echo $FTP_PROXY >> %s", testFile("proxy.output")),
1017
fmt.Sprintf("echo $no_proxy >> %s", testFile("proxy.output")),
1018
fmt.Sprintf("echo $NO_PROXY >> %s", testFile("proxy.output")),
1021
testFile("proxy.output"),
1022
"http\nhttp\nhttps\nhttps\nftp\nftp\nlocalhost\nlocalhost\n",
1025
"run commands: async using rpc client",
1027
asyncRunCommands{echoUnitNameToFile("run.output")},
1028
verifyFile{testFile("run.output"), "juju run u/0\n"},
1030
"run commands: waits for lock",
1032
acquireHookSyncLock{},
1033
asyncRunCommands{echoUnitNameToFile("wait.output")},
1034
verifyNoFile{testFile("wait.output")},
1035
releaseHookSyncLock,
1036
verifyFile{testFile("wait.output"), "juju run u/0\n"},
1041
1111
func (s *UniterSuite) TestUniterRelations(c *gc.C) {
1112
waitDyingHooks := custom{func(c *gc.C, ctx *context) {
1113
// There is no ordering relationship between relation hooks and
1114
// leader-settings-changed hooks; and while we're dying we may
1115
// never get to leader-settings-changed before it's time to run
1116
// the stop (as we might not react to a config change in time).
1117
// It's actually clearer to just list the possible orders:
1118
possibles := [][]string{{
1119
"leader-settings-changed",
1120
"db-relation-departed mysql/0 db:0",
1121
"db-relation-broken db:0",
1124
"db-relation-departed mysql/0 db:0",
1125
"leader-settings-changed",
1126
"db-relation-broken db:0",
1129
"db-relation-departed mysql/0 db:0",
1130
"db-relation-broken db:0",
1131
"leader-settings-changed",
1134
"db-relation-departed mysql/0 db:0",
1135
"db-relation-broken db:0",
1138
unchecked := ctx.hooksCompleted[len(ctx.hooks):]
1139
for _, possible := range possibles {
1140
if ok, _ := jc.DeepEqual(unchecked, possible); ok {
1144
c.Fatalf("unexpected hooks: %v", unchecked)
1042
1146
s.runUniterTests(c, []uniterTest{
1045
1149
"simple joined/changed/departed",
1046
1150
quickStartRelation{},
1047
1151
addRelationUnit{},
1048
waitHooks{"db-relation-joined mysql/1 db:0", "db-relation-changed mysql/1 db:0"},
1153
"db-relation-joined mysql/1 db:0",
1154
"db-relation-changed mysql/1 db:0",
1049
1156
changeRelationUnit{"mysql/0"},
1050
1157
waitHooks{"db-relation-changed mysql/0 db:0"},
1051
1158
removeRelationUnit{"mysql/1"},
1117
1227
"all relations are available to config-changed on bounce, even if state dir is missing",
1119
1229
customize: func(c *gc.C, ctx *context, path string) {
1120
script := "relation-ids db > relations.out && chmod 644 relations.out"
1230
script := uniterRelationsCustomizeScript
1121
1231
appendHook(c, path, "config-changed", script)
1125
1235
createUniter{},
1127
status: params.StatusActive,
1129
waitHooks{"install", "config-changed", "start"},
1237
status: params.StatusIdle,
1240
statusGetter: unitStatusGetter,
1241
status: params.StatusUnknown,
1243
waitHooks{"install", "leader-elected", "config-changed", "start"},
1130
1244
addRelation{waitJoin: true},
1132
1246
custom{func(c *gc.C, ctx *context) {
1515
1708
addAction{"action-log", nil},
1517
status: params.StatusError,
1518
info: `hook failed: "start"`,
1710
statusGetter: unitStatusGetter,
1711
status: params.StatusError,
1712
info: `hook failed: "start"`,
1519
1713
data: map[string]interface{}{
1520
1714
"hook": "start",
1523
verifyNoActionResults{},
1525
resolveError{state.ResolvedNoHooks},
1526
waitUnit{status: params.StatusActive},
1527
1717
waitActionResults{[]actionResult{{
1528
1718
name: "action-log",
1529
1719
results: map[string]interface{}{},
1530
1720
status: params.ActionCompleted,
1532
waitUnit{status: params.StatusActive},
1723
statusGetter: unitStatusGetter,
1724
status: params.StatusError,
1725
info: `hook failed: "start"`,
1726
data: map[string]interface{}{"hook": "start"},
1729
resolveError{state.ResolvedNoHooks},
1730
waitUnitAgent{status: params.StatusIdle},
1732
statusGetter: unitStatusGetter,
1733
status: params.StatusMaintenance,
1734
info: "installing charm software",
1960
func (s *UniterSuite) TestLeadership(c *gc.C) {
1961
s.runUniterTests(c, []uniterTest{
1963
"hook tools when leader",
1965
runCommands{"leader-set foo=bar baz=qux"},
1966
verifyLeaderSettings{"foo": "bar", "baz": "qux"},
1968
"hook tools when not leader",
1969
quickStart{minion: true},
1970
runCommands{leadershipScript},
1972
"leader-elected triggers when elected",
1973
quickStart{minion: true},
1975
waitHooks{"leader-elected"},
1977
"leader-settings-changed triggers when leader settings change",
1978
quickStart{minion: true},
1979
setLeaderSettings{"ping": "pong"},
1980
waitHooks{"leader-settings-changed"},
1982
"leader-settings-changed triggers when bounced",
1983
quickStart{minion: true},
1984
verifyRunning{minion: true},
1986
"leader-settings-changed triggers when deposed (while stopped)",
1990
verifyRunning{minion: true},
1995
func (s *UniterSuite) TestLeadershipUnexpectedDepose(c *gc.C) {
1996
s.PatchValue(uniter.LeadershipGuarantee, 2*coretesting.ShortWait)
1997
s.runUniterTests(c, []uniterTest{
1999
// NOTE: this is a strange and ugly test, intended to detect what
2000
// *would* happen if the uniter suddenly failed to renew its lease;
2001
// it depends on an artificially shortened tracker refresh time to
2002
// run in a reasonable amount of time.
2003
"leader-settings-changed triggers when deposed (while running)",
2006
waitHooks{"leader-settings-changed"},
2011
func (s *UniterSuite) TestStorage(c *gc.C) {
2012
// appendStorageMetadata customises the wordpress charm's metadata,
2013
// adding a "wp-content" filesystem store. We do it here rather
2014
// than in the charm itself to avoid modifying all of the other
2016
appendStorageMetadata := func(c *gc.C, ctx *context, path string) {
2017
f, err := os.OpenFile(filepath.Join(path, "metadata.yaml"), os.O_RDWR|os.O_APPEND, 0644)
2018
c.Assert(err, jc.ErrorIsNil)
2021
c.Assert(err, jc.ErrorIsNil)
2023
_, err = io.WriteString(f, "storage:\n wp-content:\n type: filesystem\n")
2024
c.Assert(err, jc.ErrorIsNil)
2026
s.runUniterTests(c, []uniterTest{
2028
"test that storage-attached is called",
2029
createCharm{customize: appendStorageMetadata},
2031
ensureStateWorker{},
2032
createServiceAndUnit{},
2036
waitHooks{"wp-content-storage-attached"},
2037
waitHooks(startupHooks(false)),
2039
"test that storage-detaching is called before stop",
2040
createCharm{customize: appendStorageMetadata},
2042
ensureStateWorker{},
2043
createServiceAndUnit{},
2047
waitHooks{"wp-content-storage-attached"},
2048
waitHooks(startupHooks(false)),
2050
waitHooks{"leader-settings-changed"},
2051
// "stop" hook is not called until storage is detached
2052
waitHooks{"wp-content-storage-detaching", "stop"},
2053
verifyStorageDetached{},
2056
"test that storage-detaching is called only if previously attached",
2057
createCharm{customize: appendStorageMetadata},
2059
ensureStateWorker{},
2060
createServiceAndUnit{},
2061
// provision and destroy the storage before the uniter starts,
2062
// to ensure it never sees the storage as attached
2064
destroyStorageAttachment{},
2066
waitHooks(startupHooks(false)),
2068
// storage-detaching is not called because it was never attached
2070
verifyStorageDetached{},
2073
"test that delay-provisioned storage does not block forever",
2074
createCharm{customize: appendStorageMetadata},
2076
ensureStateWorker{},
2077
createServiceAndUnit{},
2079
// no hooks should be run, as storage isn't provisioned
2082
waitHooks{"wp-content-storage-attached"},
2083
waitHooks(startupHooks(false)),
2085
"test that unprovisioned storage does not block unit termination",
2086
createCharm{customize: appendStorageMetadata},
2088
ensureStateWorker{},
2089
createServiceAndUnit{},
2091
// no hooks should be run, as storage isn't provisioned
2094
// TODO(axw) should we really be running startup hooks
2095
// when the unit is dying?
2096
waitHooks(startupHooks(true)),
2100
// TODO(axw) test that storage-attached is run for new
2101
// storage attachments before upgrade-charm is run. This
2102
// requires additions to state to add storage when a charm
2107
type mockExecutor struct {
2111
func (m *mockExecutor) Run(op operation.Operation) error {
2112
// want to allow charm unpacking to occur
2113
if strings.HasPrefix(op.String(), "install") {
2114
return m.Executor.Run(op)
2116
// but hooks should error
2117
return errors.New("some error occurred")
2120
func (s *UniterSuite) TestOperationErrorReported(c *gc.C) {
2121
executorFunc := func(u *uniter.Uniter) (operation.Executor, error) {
2122
e, err := uniter.NewExecutor(u)
2123
c.Assert(err, jc.ErrorIsNil)
2124
return &mockExecutor{e}, nil
2126
s.runUniterTests(c, []uniterTest{
2128
"error running operations are reported",
2131
createUniter{executorFunc: executorFunc},
2133
status: params.StatusFailed,
2134
info: "run install hook",
2136
expectError{".*some error occurred.*"},