gitee.com/mysnapcore/mysnapd@v0.1.0/daemon/api_aliases_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2014-2020 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package daemon_test 21 22 import ( 23 "bytes" 24 "context" 25 "encoding/json" 26 "net/http" 27 "net/http/httptest" 28 "os" 29 "path/filepath" 30 31 "gopkg.in/check.v1" 32 33 "gitee.com/mysnapcore/mysnapd/daemon" 34 "gitee.com/mysnapcore/mysnapd/dirs" 35 "gitee.com/mysnapcore/mysnapd/osutil" 36 "gitee.com/mysnapcore/mysnapd/overlord/snapstate" 37 "gitee.com/mysnapcore/mysnapd/overlord/state" 38 "gitee.com/mysnapcore/mysnapd/snap" 39 ) 40 41 var _ = check.Suite(&aliasesSuite{}) 42 43 type aliasesSuite struct { 44 apiBaseSuite 45 } 46 47 const aliasYaml = ` 48 name: alias-snap 49 version: 1 50 apps: 51 app: 52 app2: 53 ` 54 55 func (s *aliasesSuite) TestAliasSuccess(c *check.C) { 56 err := os.MkdirAll(dirs.SnapBinariesDir, 0755) 57 c.Assert(err, check.IsNil) 58 d := s.daemon(c) 59 60 s.mockSnap(c, aliasYaml) 61 62 oldAutoAliases := snapstate.AutoAliases 63 snapstate.AutoAliases = func(*state.State, *snap.Info) (map[string]string, error) { 64 return nil, nil 65 } 66 defer func() { snapstate.AutoAliases = oldAutoAliases }() 67 68 d.Overlord().Loop() 69 defer d.Overlord().Stop() 70 71 action := &daemon.AliasAction{ 72 Action: "alias", 73 Snap: "alias-snap", 74 App: "app", 75 Alias: "alias1", 76 } 77 text, err := json.Marshal(action) 78 c.Assert(err, check.IsNil) 79 buf := bytes.NewBuffer(text) 80 req, err := http.NewRequest("POST", "/v2/aliases", buf) 81 c.Assert(err, check.IsNil) 82 rec := httptest.NewRecorder() 83 s.req(c, req, nil).ServeHTTP(rec, req) 84 c.Assert(rec.Code, check.Equals, 202) 85 var body map[string]interface{} 86 err = json.Unmarshal(rec.Body.Bytes(), &body) 87 c.Check(err, check.IsNil) 88 id := body["change"].(string) 89 90 st := d.Overlord().State() 91 st.Lock() 92 chg := st.Change(id) 93 st.Unlock() 94 c.Assert(chg, check.NotNil) 95 96 <-chg.Ready() 97 98 st.Lock() 99 err = chg.Err() 100 st.Unlock() 101 c.Assert(err, check.IsNil) 102 103 // validity check 104 c.Check(osutil.IsSymlink(filepath.Join(dirs.SnapBinariesDir, "alias1")), check.Equals, true) 105 } 106 107 func (s *aliasesSuite) TestAliasChangeConflict(c *check.C) { 108 err := os.MkdirAll(dirs.SnapBinariesDir, 0755) 109 c.Assert(err, check.IsNil) 110 s.daemon(c) 111 112 s.mockSnap(c, aliasYaml) 113 114 s.simulateConflict("alias-snap") 115 116 oldAutoAliases := snapstate.AutoAliases 117 snapstate.AutoAliases = func(*state.State, *snap.Info) (map[string]string, error) { 118 return nil, nil 119 } 120 defer func() { snapstate.AutoAliases = oldAutoAliases }() 121 122 action := &daemon.AliasAction{ 123 Action: "alias", 124 Snap: "alias-snap", 125 App: "app", 126 Alias: "alias1", 127 } 128 text, err := json.Marshal(action) 129 c.Assert(err, check.IsNil) 130 buf := bytes.NewBuffer(text) 131 req, err := http.NewRequest("POST", "/v2/aliases", buf) 132 c.Assert(err, check.IsNil) 133 rec := httptest.NewRecorder() 134 s.req(c, req, nil).ServeHTTP(rec, req) 135 c.Check(rec.Code, check.Equals, 409) 136 137 var body map[string]interface{} 138 err = json.Unmarshal(rec.Body.Bytes(), &body) 139 c.Check(err, check.IsNil) 140 c.Check(body, check.DeepEquals, map[string]interface{}{ 141 "status-code": 409., 142 "status": "Conflict", 143 "result": map[string]interface{}{ 144 "message": `snap "alias-snap" has "manip" change in progress`, 145 "kind": "snap-change-conflict", 146 "value": map[string]interface{}{ 147 "change-kind": "manip", 148 "snap-name": "alias-snap", 149 }, 150 }, 151 "type": "error"}) 152 } 153 154 func (s *aliasesSuite) TestAliasErrors(c *check.C) { 155 s.daemon(c) 156 157 errScenarios := []struct { 158 mangle func(*daemon.AliasAction) 159 err string 160 }{ 161 {func(a *daemon.AliasAction) { a.Action = "" }, `unsupported alias action: ""`}, 162 {func(a *daemon.AliasAction) { a.Action = "what" }, `unsupported alias action: "what"`}, 163 {func(a *daemon.AliasAction) { a.Snap = "lalala" }, `snap "lalala" is not installed`}, 164 {func(a *daemon.AliasAction) { a.Alias = ".foo" }, `invalid alias name: ".foo"`}, 165 {func(a *daemon.AliasAction) { a.Aliases = []string{"baz"} }, `cannot interpret request, snaps can no longer be expected to declare their aliases`}, 166 } 167 168 for _, scen := range errScenarios { 169 action := &daemon.AliasAction{ 170 Action: "alias", 171 Snap: "alias-snap", 172 App: "app", 173 Alias: "alias1", 174 } 175 scen.mangle(action) 176 177 text, err := json.Marshal(action) 178 c.Assert(err, check.IsNil) 179 buf := bytes.NewBuffer(text) 180 req, err := http.NewRequest("POST", "/v2/aliases", buf) 181 c.Assert(err, check.IsNil) 182 183 rspe := s.errorReq(c, req, nil) 184 c.Check(rspe.Status, check.Equals, 400) 185 c.Check(rspe.Message, check.Matches, scen.err) 186 } 187 } 188 189 func (s *aliasesSuite) TestUnaliasSnapSuccess(c *check.C) { 190 err := os.MkdirAll(dirs.SnapBinariesDir, 0755) 191 c.Assert(err, check.IsNil) 192 d := s.daemon(c) 193 194 s.mockSnap(c, aliasYaml) 195 196 oldAutoAliases := snapstate.AutoAliases 197 snapstate.AutoAliases = func(*state.State, *snap.Info) (map[string]string, error) { 198 return nil, nil 199 } 200 defer func() { snapstate.AutoAliases = oldAutoAliases }() 201 202 d.Overlord().Loop() 203 defer d.Overlord().Stop() 204 205 action := &daemon.AliasAction{ 206 Action: "unalias", 207 Snap: "alias-snap", 208 } 209 text, err := json.Marshal(action) 210 c.Assert(err, check.IsNil) 211 buf := bytes.NewBuffer(text) 212 req, err := http.NewRequest("POST", "/v2/aliases", buf) 213 c.Assert(err, check.IsNil) 214 rec := httptest.NewRecorder() 215 s.req(c, req, nil).ServeHTTP(rec, req) 216 c.Assert(rec.Code, check.Equals, 202) 217 var body map[string]interface{} 218 err = json.Unmarshal(rec.Body.Bytes(), &body) 219 c.Check(err, check.IsNil) 220 id := body["change"].(string) 221 222 st := d.Overlord().State() 223 st.Lock() 224 chg := st.Change(id) 225 c.Check(chg.Summary(), check.Equals, `Disable all aliases for snap "alias-snap"`) 226 st.Unlock() 227 c.Assert(chg, check.NotNil) 228 229 <-chg.Ready() 230 231 st.Lock() 232 defer st.Unlock() 233 err = chg.Err() 234 c.Assert(err, check.IsNil) 235 236 // validity check 237 var snapst snapstate.SnapState 238 err = snapstate.Get(st, "alias-snap", &snapst) 239 c.Assert(err, check.IsNil) 240 c.Check(snapst.AutoAliasesDisabled, check.Equals, true) 241 } 242 243 func (s *aliasesSuite) TestUnaliasDWIMSnapSuccess(c *check.C) { 244 err := os.MkdirAll(dirs.SnapBinariesDir, 0755) 245 c.Assert(err, check.IsNil) 246 d := s.daemon(c) 247 248 s.mockSnap(c, aliasYaml) 249 250 oldAutoAliases := snapstate.AutoAliases 251 snapstate.AutoAliases = func(*state.State, *snap.Info) (map[string]string, error) { 252 return nil, nil 253 } 254 defer func() { snapstate.AutoAliases = oldAutoAliases }() 255 256 d.Overlord().Loop() 257 defer d.Overlord().Stop() 258 259 action := &daemon.AliasAction{ 260 Action: "unalias", 261 Snap: "alias-snap", 262 Alias: "alias-snap", 263 } 264 text, err := json.Marshal(action) 265 c.Assert(err, check.IsNil) 266 buf := bytes.NewBuffer(text) 267 req, err := http.NewRequest("POST", "/v2/aliases", buf) 268 c.Assert(err, check.IsNil) 269 rec := httptest.NewRecorder() 270 s.req(c, req, nil).ServeHTTP(rec, req) 271 c.Assert(rec.Code, check.Equals, 202) 272 var body map[string]interface{} 273 err = json.Unmarshal(rec.Body.Bytes(), &body) 274 c.Check(err, check.IsNil) 275 id := body["change"].(string) 276 277 st := d.Overlord().State() 278 st.Lock() 279 chg := st.Change(id) 280 c.Check(chg.Summary(), check.Equals, `Disable all aliases for snap "alias-snap"`) 281 st.Unlock() 282 c.Assert(chg, check.NotNil) 283 284 <-chg.Ready() 285 286 st.Lock() 287 defer st.Unlock() 288 err = chg.Err() 289 c.Assert(err, check.IsNil) 290 291 // validity check 292 var snapst snapstate.SnapState 293 err = snapstate.Get(st, "alias-snap", &snapst) 294 c.Assert(err, check.IsNil) 295 c.Check(snapst.AutoAliasesDisabled, check.Equals, true) 296 } 297 298 func (s *aliasesSuite) TestUnaliasAliasSuccess(c *check.C) { 299 err := os.MkdirAll(dirs.SnapBinariesDir, 0755) 300 c.Assert(err, check.IsNil) 301 d := s.daemon(c) 302 303 s.mockSnap(c, aliasYaml) 304 305 oldAutoAliases := snapstate.AutoAliases 306 snapstate.AutoAliases = func(*state.State, *snap.Info) (map[string]string, error) { 307 return nil, nil 308 } 309 defer func() { snapstate.AutoAliases = oldAutoAliases }() 310 311 d.Overlord().Loop() 312 defer d.Overlord().Stop() 313 314 action := &daemon.AliasAction{ 315 Action: "alias", 316 Snap: "alias-snap", 317 App: "app", 318 Alias: "alias1", 319 } 320 text, err := json.Marshal(action) 321 c.Assert(err, check.IsNil) 322 buf := bytes.NewBuffer(text) 323 req, err := http.NewRequest("POST", "/v2/aliases", buf) 324 c.Assert(err, check.IsNil) 325 rec := httptest.NewRecorder() 326 s.req(c, req, nil).ServeHTTP(rec, req) 327 c.Assert(rec.Code, check.Equals, 202) 328 var body map[string]interface{} 329 err = json.Unmarshal(rec.Body.Bytes(), &body) 330 c.Check(err, check.IsNil) 331 id := body["change"].(string) 332 333 st := d.Overlord().State() 334 st.Lock() 335 chg := st.Change(id) 336 st.Unlock() 337 c.Assert(chg, check.NotNil) 338 339 <-chg.Ready() 340 341 st.Lock() 342 err = chg.Err() 343 st.Unlock() 344 c.Assert(err, check.IsNil) 345 346 // unalias 347 action = &daemon.AliasAction{ 348 Action: "unalias", 349 Alias: "alias1", 350 } 351 text, err = json.Marshal(action) 352 c.Assert(err, check.IsNil) 353 buf = bytes.NewBuffer(text) 354 req, err = http.NewRequest("POST", "/v2/aliases", buf) 355 c.Assert(err, check.IsNil) 356 rec = httptest.NewRecorder() 357 s.req(c, req, nil).ServeHTTP(rec, req) 358 c.Assert(rec.Code, check.Equals, 202) 359 err = json.Unmarshal(rec.Body.Bytes(), &body) 360 c.Check(err, check.IsNil) 361 id = body["change"].(string) 362 363 st.Lock() 364 chg = st.Change(id) 365 c.Check(chg.Summary(), check.Equals, `Remove manual alias "alias1" for snap "alias-snap"`) 366 st.Unlock() 367 c.Assert(chg, check.NotNil) 368 369 <-chg.Ready() 370 371 st.Lock() 372 defer st.Unlock() 373 err = chg.Err() 374 c.Assert(err, check.IsNil) 375 376 // validity check 377 c.Check(osutil.FileExists(filepath.Join(dirs.SnapBinariesDir, "alias1")), check.Equals, false) 378 } 379 380 func (s *aliasesSuite) TestUnaliasDWIMAliasSuccess(c *check.C) { 381 err := os.MkdirAll(dirs.SnapBinariesDir, 0755) 382 c.Assert(err, check.IsNil) 383 d := s.daemon(c) 384 385 s.mockSnap(c, aliasYaml) 386 387 oldAutoAliases := snapstate.AutoAliases 388 snapstate.AutoAliases = func(*state.State, *snap.Info) (map[string]string, error) { 389 return nil, nil 390 } 391 defer func() { snapstate.AutoAliases = oldAutoAliases }() 392 393 d.Overlord().Loop() 394 defer d.Overlord().Stop() 395 396 action := &daemon.AliasAction{ 397 Action: "alias", 398 Snap: "alias-snap", 399 App: "app", 400 Alias: "alias1", 401 } 402 text, err := json.Marshal(action) 403 c.Assert(err, check.IsNil) 404 buf := bytes.NewBuffer(text) 405 req, err := http.NewRequest("POST", "/v2/aliases", buf) 406 c.Assert(err, check.IsNil) 407 rec := httptest.NewRecorder() 408 s.req(c, req, nil).ServeHTTP(rec, req) 409 c.Assert(rec.Code, check.Equals, 202) 410 var body map[string]interface{} 411 err = json.Unmarshal(rec.Body.Bytes(), &body) 412 c.Check(err, check.IsNil) 413 id := body["change"].(string) 414 415 st := d.Overlord().State() 416 st.Lock() 417 chg := st.Change(id) 418 st.Unlock() 419 c.Assert(chg, check.NotNil) 420 421 <-chg.Ready() 422 423 st.Lock() 424 err = chg.Err() 425 st.Unlock() 426 c.Assert(err, check.IsNil) 427 428 // DWIM unalias an alias 429 action = &daemon.AliasAction{ 430 Action: "unalias", 431 Snap: "alias1", 432 Alias: "alias1", 433 } 434 text, err = json.Marshal(action) 435 c.Assert(err, check.IsNil) 436 buf = bytes.NewBuffer(text) 437 req, err = http.NewRequest("POST", "/v2/aliases", buf) 438 c.Assert(err, check.IsNil) 439 rec = httptest.NewRecorder() 440 s.req(c, req, nil).ServeHTTP(rec, req) 441 c.Assert(rec.Code, check.Equals, 202) 442 err = json.Unmarshal(rec.Body.Bytes(), &body) 443 c.Check(err, check.IsNil) 444 id = body["change"].(string) 445 446 st.Lock() 447 chg = st.Change(id) 448 c.Check(chg.Summary(), check.Equals, `Remove manual alias "alias1" for snap "alias-snap"`) 449 st.Unlock() 450 c.Assert(chg, check.NotNil) 451 452 <-chg.Ready() 453 454 st.Lock() 455 defer st.Unlock() 456 err = chg.Err() 457 c.Assert(err, check.IsNil) 458 459 // validity check 460 c.Check(osutil.FileExists(filepath.Join(dirs.SnapBinariesDir, "alias1")), check.Equals, false) 461 } 462 463 func (s *aliasesSuite) TestPreferSuccess(c *check.C) { 464 err := os.MkdirAll(dirs.SnapBinariesDir, 0755) 465 c.Assert(err, check.IsNil) 466 d := s.daemon(c) 467 468 s.mockSnap(c, aliasYaml) 469 470 oldAutoAliases := snapstate.AutoAliases 471 snapstate.AutoAliases = func(*state.State, *snap.Info) (map[string]string, error) { 472 return nil, nil 473 } 474 defer func() { snapstate.AutoAliases = oldAutoAliases }() 475 476 d.Overlord().Loop() 477 defer d.Overlord().Stop() 478 479 action := &daemon.AliasAction{ 480 Action: "prefer", 481 Snap: "alias-snap", 482 } 483 text, err := json.Marshal(action) 484 c.Assert(err, check.IsNil) 485 buf := bytes.NewBuffer(text) 486 req, err := http.NewRequest("POST", "/v2/aliases", buf) 487 c.Assert(err, check.IsNil) 488 rec := httptest.NewRecorder() 489 s.req(c, req, nil).ServeHTTP(rec, req) 490 c.Assert(rec.Code, check.Equals, 202) 491 var body map[string]interface{} 492 err = json.Unmarshal(rec.Body.Bytes(), &body) 493 c.Check(err, check.IsNil) 494 id := body["change"].(string) 495 496 st := d.Overlord().State() 497 st.Lock() 498 chg := st.Change(id) 499 c.Check(chg.Summary(), check.Equals, `Prefer aliases of snap "alias-snap"`) 500 st.Unlock() 501 c.Assert(chg, check.NotNil) 502 503 <-chg.Ready() 504 505 st.Lock() 506 defer st.Unlock() 507 err = chg.Err() 508 c.Assert(err, check.IsNil) 509 510 // validity check 511 var snapst snapstate.SnapState 512 err = snapstate.Get(st, "alias-snap", &snapst) 513 c.Assert(err, check.IsNil) 514 c.Check(snapst.AutoAliasesDisabled, check.Equals, false) 515 } 516 517 func (s *aliasesSuite) TestAliases(c *check.C) { 518 d := s.daemon(c) 519 520 st := d.Overlord().State() 521 st.Lock() 522 snapstate.Set(st, "alias-snap1", &snapstate.SnapState{ 523 Sequence: []*snap.SideInfo{ 524 {RealName: "alias-snap1", Revision: snap.R(11)}, 525 }, 526 Current: snap.R(11), 527 Active: true, 528 Aliases: map[string]*snapstate.AliasTarget{ 529 "alias1": {Manual: "cmd1x", Auto: "cmd1"}, 530 "alias2": {Auto: "cmd2"}, 531 }, 532 }) 533 snapstate.Set(st, "alias-snap2", &snapstate.SnapState{ 534 Sequence: []*snap.SideInfo{ 535 {RealName: "alias-snap2", Revision: snap.R(12)}, 536 }, 537 Current: snap.R(12), 538 Active: true, 539 AutoAliasesDisabled: true, 540 Aliases: map[string]*snapstate.AliasTarget{ 541 "alias2": {Auto: "cmd2"}, 542 "alias3": {Manual: "cmd3"}, 543 "alias4": {Manual: "cmd4x", Auto: "cmd4"}, 544 }, 545 }) 546 st.Unlock() 547 548 req, err := http.NewRequest("GET", "/v2/aliases", nil) 549 c.Assert(err, check.IsNil) 550 551 rsp := s.syncReq(c, req, nil) 552 c.Check(rsp.Status, check.Equals, 200) 553 c.Check(rsp.Result, check.DeepEquals, map[string]map[string]daemon.AliasStatus{ 554 "alias-snap1": { 555 "alias1": { 556 Command: "alias-snap1.cmd1x", 557 Status: "manual", 558 Manual: "cmd1x", 559 Auto: "cmd1", 560 }, 561 "alias2": { 562 Command: "alias-snap1.cmd2", 563 Status: "auto", 564 Auto: "cmd2", 565 }, 566 }, 567 "alias-snap2": { 568 "alias2": { 569 Command: "alias-snap2.cmd2", 570 Status: "disabled", 571 Auto: "cmd2", 572 }, 573 "alias3": { 574 Command: "alias-snap2.cmd3", 575 Status: "manual", 576 Manual: "cmd3", 577 }, 578 "alias4": { 579 Command: "alias-snap2.cmd4x", 580 Status: "manual", 581 Manual: "cmd4x", 582 Auto: "cmd4", 583 }, 584 }, 585 }) 586 587 } 588 589 func (s *aliasesSuite) TestInstallUnaliased(c *check.C) { 590 var calledFlags snapstate.Flags 591 592 defer daemon.MockSnapstateInstall(func(ctx context.Context, s *state.State, name string, opts *snapstate.RevisionOptions, userID int, flags snapstate.Flags) (*state.TaskSet, error) { 593 calledFlags = flags 594 595 t := s.NewTask("fake-install-snap", "Doing a fake install") 596 return state.NewTaskSet(t), nil 597 })() 598 599 d := s.daemon(c) 600 inst := &daemon.SnapInstruction{ 601 Action: "install", 602 // Install the snap without enabled automatic aliases 603 Unaliased: true, 604 Snaps: []string{"fake"}, 605 } 606 607 st := d.Overlord().State() 608 st.Lock() 609 defer st.Unlock() 610 _, _, err := inst.Dispatch()(inst, st) 611 c.Check(err, check.IsNil) 612 613 c.Check(calledFlags.Unaliased, check.Equals, true) 614 } 615 616 func (s *aliasesSuite) TestInstallIgnoreRunning(c *check.C) { 617 var calledFlags snapstate.Flags 618 619 defer daemon.MockSnapstateInstall(func(ctx context.Context, s *state.State, name string, opts *snapstate.RevisionOptions, userID int, flags snapstate.Flags) (*state.TaskSet, error) { 620 calledFlags = flags 621 622 t := s.NewTask("fake-install-snap", "Doing a fake install") 623 return state.NewTaskSet(t), nil 624 })() 625 626 d := s.daemon(c) 627 inst := &daemon.SnapInstruction{ 628 Action: "install", 629 // Install the snap without enabled automatic aliases 630 IgnoreRunning: true, 631 Snaps: []string{"fake"}, 632 } 633 634 st := d.Overlord().State() 635 st.Lock() 636 defer st.Unlock() 637 _, _, err := inst.Dispatch()(inst, st) 638 c.Check(err, check.IsNil) 639 640 c.Check(calledFlags.IgnoreRunning, check.Equals, true) 641 }