Skip to content

Commit

Permalink
merged lp:snappy and resolved conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
mvo5 committed Jul 24, 2015
2 parents 646e7b6 + 22a6010 commit aedc13b
Show file tree
Hide file tree
Showing 78 changed files with 1,765 additions and 827 deletions.
52 changes: 50 additions & 2 deletions _integration-tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,31 @@

* Internet access in the test bed.

## Setting up the project

First you need to set up the GOPATH, get the snappy sources and the
dependencies as explained in the `README.md` that is located at the root of the
branch.

## Testing a virtual machine

You can execute the full integration suite in a local virtual machine with:

go run _integration-test/main.go
go run _integration-tests/main.go

The test runner will create the snappy images with `ubuntu-device-flash`, so it
will ask for your password to run this command with `sudo`.

You can also especify more options to customize the image being created, including
the release, the channel and the revision to use. This parameters will be passed
to `ubuntu-device-flash`:

go run _integration-tests/main.go -release 15.04 -channel stable -revision 3

The default values are suited to testing the most recent version, `rolling` for
release, `edge` for channel and an empty revision, which picks the latest
available.

## Testing snappy from a branch

With the --snappy-from-branch flag, the snappy CLI command will be compiled
Expand All @@ -39,7 +55,7 @@ pass MyTestSuite, MyTestSuite.FirstCustomTest or MyTestSuite.*CustomTest:

You can execute the integration suite in a remote snappy machine with:

go run _integration-test/main.go --ip {testbed-ip} --port {testbed-port} \
go run _integration-tests/main.go --ip {testbed-ip} --port {testbed-port} \
--arch {testbed-arch}

The test runner will use `ssh-copy-id` to send your identity file to the
Expand Down Expand Up @@ -67,3 +83,35 @@ same network as the test runner host, and find the {beaglebone-ip}.
Run the tests with:

go run _integration-tests/main.go --ip {beaglebone-ip} --arch arm

## Testing an update

With the --update flag you can flash an old image, update to the latest and
then run the whole suite on the updated system. The release, the channel and
the revision flags specify the image that will be flashed, and the
target-release and target-channel flags specify the values to be used in the
update if they are different from the flashed values.

For example, to update from rolling edge -1 to the latest and then run the
integration tests:

go run _integration-tests/main.go --snappy-from-branch \
--revision=-1 --update

To update from 15.04 alpha to rolling edge and then run the integration tests:

go run _integration-tests/main.go --snappy-from-branch \
--release=15.04 --channel=alpha \
--update --target-release=rolling --target-channel=edge

## Testing a rollback

With the --rollback flag you can flash an old image, update to the latest,
rollback again to the old image and then run the whole suite on the rolled
back system. You should use the release, channel, revision, target-release and
target-channel flags as when testing an update.

For example, to test a rollback from latest rolling edge to rolling edge -1:

go run _integration-tests/main.go \
--revision=-1 --rollback
10 changes: 2 additions & 8 deletions _integration-tests/data/tpl/control
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
{{ $filter := .Filter }}
{{ range $element := .Tests }}
Test-Command: ./_integration-tests/reboot-wrapper {{ $element }}.test {{ if $filter }}-gocheck.f {{ $filter }}{{ end }}
{{ $test := .Test }}
Test-Command: ./_integration-tests/reboot-wrapper {{ $test }} {{ if $filter }}-gocheck.f {{ $filter }}{{ end }}
Restrictions: allow-stderr
{{ end }}

{{ if .IncludeShell }}
Test-Command: ./_integration-tests/snappy-selftest --yes-really
Depends:
{{ end }}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import (
"strings"

check "gopkg.in/check.v1"

"launchpad.net/snappy/_integration-tests/helpers/config"
)

const (
Expand All @@ -39,6 +41,10 @@ const (
channelCfgFile = "/etc/system-image/channel.ini"
)

// Cfg is a struct that contains the configurations values passed from the
// host to the testbed.
var Cfg config.Config

// SnappySuite is a structure used as a base test suite for all the snappy
// integration tests.
type SnappySuite struct {
Expand All @@ -50,6 +56,28 @@ type SnappySuite struct {
func (s *SnappySuite) SetUpSuite(c *check.C) {
ExecCommand(c, "sudo", "systemctl", "stop", "snappy-autopilot.timer")
ExecCommand(c, "sudo", "systemctl", "disable", "snappy-autopilot.timer")
if !isInRebootProcess() {
Cfg, err := config.ReadConfig(
"_integration-tests/data/output/testconfig.json")
c.Assert(err, check.IsNil, check.Commentf("Error reading config: %v", err))
if Cfg.Update || Cfg.Rollback {
switchSystemImageConf(c, Cfg.TargetRelease, Cfg.TargetChannel, "0")
// Always use the installed snappy because we are updating from an old
// image, so we should not use the snappy from the branch.
output := ExecCommand(c, "sudo", "/usr/bin/snappy", "update")
if output != "" {
RebootWithMark(c, "setupsuite-update")
}
}
} else if CheckRebootMark("setupsuite-update") {
RemoveRebootMark(c)
if Cfg.Rollback {
ExecCommand(c, "sudo", "snappy", "rollback", "ubuntu-core")
RebootWithMark(c, "setupsuite-rollback")
}
} else if CheckRebootMark("setupsuite-rollback") {
RemoveRebootMark(c)
}
}

// SetUpTest handles reboots and stores version information. It will run before
Expand All @@ -58,15 +86,15 @@ func (s *SnappySuite) SetUpSuite(c *check.C) {
// will skip all the following tests. If the suite is being called after the
// test bed was rebooted, it will resume the test that requested the reboot.
func (s *SnappySuite) SetUpTest(c *check.C) {
if needsReboot() {
if NeedsReboot() {
contents, err := ioutil.ReadFile(needsRebootFile)
c.Assert(err, check.IsNil, check.Commentf("Error reading needs-reboot file %v", err))
c.Skip(fmt.Sprintf("****** Skipped %s during reboot caused by %s",
c.TestName(), contents))
} else {
if checkRebootMark("") {
if CheckRebootMark("") {
c.Logf("****** Running %s", c.TestName())
SetSavedVersion(c, GetCurrentVersion(c))
SetSavedVersion(c, GetCurrentUbuntuCoreVersion(c))
} else {
if AfterReboot(c) {
c.Logf("****** Resuming %s after reboot", c.TestName())
Expand All @@ -84,7 +112,7 @@ func (s *SnappySuite) SetUpTest(c *check.C) {
// the test.
// It also runs the cleanup handlers
func (s *SnappySuite) TearDownTest(c *check.C) {
if !needsReboot() && checkRebootMark("") {
if !NeedsReboot() && CheckRebootMark("") {
// Only restore the channel config files if the reboot has been handled.
m := make(map[string]string)
m[channelCfgBackupFile()] = "/"
Expand Down Expand Up @@ -112,6 +140,35 @@ func (s *SnappySuite) AddCleanup(f func()) {
s.cleanupHandlers = append(s.cleanupHandlers, f)
}

func switchSystemImageConf(c *check.C, release, channel, version string) {
targets := []string{"/", BaseOtherPath}
for _, target := range targets {
file := filepath.Join(target, channelCfgFile)
if _, err := os.Stat(file); err == nil {
MakeWritable(c, target)
defer MakeReadonly(c, target)
replaceSystemImageValues(c, file, release, channel, version)
}
}
}

func replaceSystemImageValues(c *check.C, file, release, channel, version string) {
c.Log("Switching the system image conf...")
replaceRegex := map[string]string{
release: `s#channel: ubuntu-core/.*/\(.*\)#channel: ubuntu-core/%s/\1#`,
channel: `s#channel: ubuntu-core/\(.*\)/.*#channel: ubuntu-core/\1/%s#`,
version: `s/build_number: .*/build_number: %s/`,
}
for value, regex := range replaceRegex {
if value != "" {
ExecCommand(c,
"sudo", "sed", "-i", fmt.Sprintf(regex, value), file)
}
}
// Leave the new file in the test log.
ExecCommand(c, "cat", file)
}

func channelCfgBackupFile() string {
return filepath.Join(os.Getenv("ADT_ARTIFACTS"), "channel.ini")
}
Expand Down Expand Up @@ -146,44 +203,43 @@ func ExecCommandToFile(c *check.C, filename string, cmds ...string) {
c.Assert(err, check.IsNil, check.Commentf("Error executing command '%v': %v", cmds, err))
}

// GetCurrentVersion returns the version number of the installed and active
// ubuntu-core.
func GetCurrentVersion(c *check.C) int {
// GetCurrentVersion returns the version of the installed and active package.
func GetCurrentVersion(c *check.C, packageName string) string {
output := ExecCommand(c, "snappy", "list")
pattern := "(?mU)^ubuntu-core (.*)$"
pattern := "(?mU)^" + packageName + " +(.*)$"
re := regexp.MustCompile(pattern)
match := re.FindStringSubmatch(string(output))
c.Assert(match, check.NotNil, check.Commentf("Version not found in %s", output))

// match is like "ubuntu-core 2015-06-18 93 ubuntu"
items := strings.Fields(match[0])
version, err := strconv.Atoi(items[2])
return items[2]
}

// GetCurrentUbuntuCoreVersion returns the version number of the installed and
// active ubuntu-core.
func GetCurrentUbuntuCoreVersion(c *check.C) int {
versionString := GetCurrentVersion(c, "ubuntu-core")
version, err := strconv.Atoi(versionString)
c.Assert(err, check.IsNil, check.Commentf("Error converting version to int %v", version))
return version
}

// CallUpdate executes an snappy update. If there is no update available, the
// channel version will be modified to fake an update.
func CallUpdate(c *check.C) {
c.Log("Calling snappy update...")
output := ExecCommand(c, "sudo", "snappy", "update")
// XXX Instead of trying the update, we should have a command to tell us
// if there is an available update. --elopio - 2015-07-01
if output == "" {
c.Log("There is no update available.")
fakeAvailableUpdate(c)
ExecCommand(c, "sudo", "snappy", "update")
}
// CallFakeUpdate calls snappy update after faking the current version
func CallFakeUpdate(c *check.C) {
c.Log("Preparing fake and calling update.")
fakeAvailableUpdate(c)
ExecCommand(c, "sudo", "snappy", "update")
}

func fakeAvailableUpdate(c *check.C) {
c.Log("Faking an available update...")
currentVersion := GetCurrentVersion(c)
switchChannelVersion(c, currentVersion, currentVersion-1)
currentVersion := GetCurrentUbuntuCoreVersion(c)
switchChannelVersionWithBackup(c, currentVersion-1)
SetSavedVersion(c, currentVersion-1)
}

func switchChannelVersion(c *check.C, oldVersion, newVersion int) {
func switchChannelVersionWithBackup(c *check.C, newVersion int) {
m := make(map[string]string)
m["/"] = channelCfgBackupFile()
m[BaseOtherPath] = channelCfgOtherBackupFile()
Expand All @@ -194,12 +250,7 @@ func switchChannelVersion(c *check.C, oldVersion, newVersion int) {
defer MakeReadonly(c, target)
// Back up the file. It will be restored during the test tear down.
ExecCommand(c, "cp", file, backup)
ExecCommand(c,
"sudo", "sed", "-i",
fmt.Sprintf(
"s/build_number: %d/build_number: %d/g",
oldVersion, newVersion),
file)
replaceSystemImageValues(c, file, "", "", strconv.Itoa(newVersion))
}
}
}
Expand All @@ -226,28 +277,35 @@ func RebootWithMark(c *check.C, mark string) {
c.Assert(err, check.IsNil, check.Commentf("Error writing needs-reboot file: %v", err))
}

func needsReboot() bool {
// NeedsReboot returns True if a reboot has been requested by a test.
func NeedsReboot() bool {
_, err := os.Stat(needsRebootFile)
return err == nil
}

// BeforeReboot returns True if the test is running before the test bed has
// been rebooted, or after the test that requested the reboot handled it.
func BeforeReboot() bool {
return checkRebootMark("")
return CheckRebootMark("")
}

// AfterReboot returns True if the test is running after the test bed has been
// rebooted.
func AfterReboot(c *check.C) bool {
// $ADT_REBOOT_MARK contains the reboot mark, if we have rebooted it'll be the test name
return checkRebootMark(c.TestName())
return strings.HasPrefix(os.Getenv("ADT_REBOOT_MARK"), c.TestName())
}

func checkRebootMark(mark string) bool {
// CheckRebootMark returns True if the reboot mark matches the string passed as
// argument.
func CheckRebootMark(mark string) bool {
return os.Getenv("ADT_REBOOT_MARK") == mark
}

func isInRebootProcess() bool {
return !CheckRebootMark("") || NeedsReboot()
}

// RemoveRebootMark removes the reboot mark to signal that the reboot has been
// handled.
func RemoveRebootMark(c *check.C) {
Expand Down
74 changes: 74 additions & 0 deletions _integration-tests/helpers/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// -*- Mode: Go; indent-tabs-mode: t -*-

/*
* Copyright (C) 2015 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

package config

import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
)

// Config contains the values to pass to the test bed from the host.
type Config struct {
FileName string
Release string
Channel string
TargetRelease string
TargetChannel string
Update bool
Rollback bool
}

// NewConfig is the Config constructor
func NewConfig(fileName, release, channel, targetRelease, targetChannel string, update, rollback bool) *Config {
return &Config{
FileName: fileName, Release: release, Channel: channel,
TargetRelease: targetRelease, TargetChannel: targetChannel,
Update: update, Rollback: rollback,
}
}

// Write writes the config to a file that will be copied to the test bed.
func (cfg Config) Write() {
fmt.Println("Writing test config...")
fmt.Println(cfg)
encoded, err := json.Marshal(cfg)
if err != nil {
log.Panicf("Error encoding the test config: %v", err)
}
err = ioutil.WriteFile(cfg.FileName, encoded, 0644)
if err != nil {
log.Panicf("Error writing the test config: %v", err)
}
}

// ReadConfig the config from a file
func ReadConfig(fileName string) (*Config, error) {
b, err := ioutil.ReadFile(fileName)
if err != nil {
return nil, err
}
var decoded Config
if err = json.Unmarshal(b, &decoded); err != nil {
return nil, err
}
return &decoded, nil
}
Loading

0 comments on commit aedc13b

Please sign in to comment.