github.com/coreos/mantle@v0.13.0/kola/tests/update/update.go (about)

     1  // Copyright 2018 CoreOS, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package update
    16  
    17  import (
    18  	"bufio"
    19  	"fmt"
    20  	"os"
    21  	"strings"
    22  	"time"
    23  
    24  	"github.com/coreos/go-omaha/omaha"
    25  
    26  	"github.com/coreos/mantle/kola"
    27  	"github.com/coreos/mantle/kola/cluster"
    28  	"github.com/coreos/mantle/kola/register"
    29  	tutil "github.com/coreos/mantle/kola/tests/util"
    30  	"github.com/coreos/mantle/platform"
    31  	"github.com/coreos/mantle/platform/local"
    32  	"github.com/coreos/mantle/util"
    33  )
    34  
    35  func init() {
    36  	register.Register(&register.Test{
    37  		Name:        "cl.update.payload",
    38  		Run:         payload,
    39  		ClusterSize: 1,
    40  		NativeFuncs: map[string]func() error{
    41  			"Omaha": Serve,
    42  		},
    43  		Distros: []string{"cl"},
    44  	})
    45  }
    46  
    47  func Serve() error {
    48  	omahaserver, err := omaha.NewTrivialServer(":34567")
    49  	if err != nil {
    50  		return fmt.Errorf("creating trivial omaha server: %v\n", err)
    51  	}
    52  
    53  	omahawrapper := local.OmahaWrapper{TrivialServer: omahaserver}
    54  
    55  	if err = omahawrapper.AddPackage("/updates/update.gz", "update.gz"); err != nil {
    56  		return fmt.Errorf("bad payload: %v", err)
    57  	}
    58  
    59  	return omahawrapper.Serve()
    60  }
    61  
    62  func payload(c cluster.TestCluster) {
    63  	addr := configureOmahaServer(c, c.Machines()[0])
    64  
    65  	// create the actual test machine, the machine
    66  	// that is created by the test registration is
    67  	// used to host the omaha server
    68  	m, err := c.NewMachine(nil)
    69  	if err != nil {
    70  		c.Fatalf("creating test machine: %v", err)
    71  	}
    72  
    73  	// Machines are intentionally configured post-boot
    74  	// via SSH to allow for testing versions which predate
    75  	// Ignition
    76  	configureMachineForUpdate(c, m, addr)
    77  
    78  	tutil.AssertBootedUsr(c, m, "USR-A")
    79  
    80  	updateMachine(c, m)
    81  
    82  	tutil.AssertBootedUsr(c, m, "USR-B")
    83  
    84  	tutil.InvalidateUsrPartition(c, m, "USR-A")
    85  
    86  	updateMachine(c, m)
    87  
    88  	tutil.AssertBootedUsr(c, m, "USR-A")
    89  }
    90  
    91  func configureOmahaServer(c cluster.TestCluster, srv platform.Machine) string {
    92  	if kola.UpdatePayloadFile == "" {
    93  		c.Skip("no update payload provided")
    94  	}
    95  
    96  	in, err := os.Open(kola.UpdatePayloadFile)
    97  	if err != nil {
    98  		c.Fatalf("opening update payload: %v", err)
    99  	}
   100  	defer in.Close()
   101  	if err := platform.InstallFile(in, srv, "/updates/update.gz"); err != nil {
   102  		c.Fatalf("copying update payload to omaha server: %v", err)
   103  	}
   104  
   105  	c.MustSSH(srv, fmt.Sprintf("sudo systemd-run --quiet ./kolet run %s Omaha", c.H.Name()))
   106  
   107  	err = util.WaitUntilReady(60*time.Second, 5*time.Second, func() (bool, error) {
   108  		_, _, err := srv.SSH(fmt.Sprintf("curl %s:34567", srv.PrivateIP()))
   109  		return err == nil, nil
   110  	})
   111  	if err != nil {
   112  		c.Fatal("timed out waiting for omaha server to become active")
   113  	}
   114  
   115  	return fmt.Sprintf("%s:34567", srv.PrivateIP())
   116  }
   117  
   118  func configureMachineForUpdate(c cluster.TestCluster, m platform.Machine, addr string) {
   119  	// update atomicly so nothing reading update.conf fails
   120  	c.MustSSH(m, fmt.Sprintf(`sudo bash -c "cat >/etc/coreos/update.conf.new <<EOF
   121  GROUP=developer
   122  SERVER=http://%s/v1/update
   123  EOF"`, addr))
   124  	c.MustSSH(m, "sudo mv /etc/coreos/update.conf{.new,}")
   125  
   126  	// inject dev key
   127  	c.MustSSH(m, `sudo bash -c "cat >/etc/coreos/update-payload-key.pub.pem <<EOF
   128  -----BEGIN PUBLIC KEY-----
   129  MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzFS5uVJ+pgibcFLD3kbY
   130  k02Edj0HXq31ZT/Bva1sLp3Ysv+QTv/ezjf0gGFfASdgpz6G+zTipS9AIrQr0yFR
   131  +tdp1ZsHLGxVwvUoXFftdapqlyj8uQcWjjbN7qJsZu0Ett/qo93hQ5nHW7Sv5dRm
   132  /ZsDFqk2Uvyaoef4bF9r03wYpZq7K3oALZ2smETv+A5600mj1Xg5M52QFU67UHls
   133  EFkZphrGjiqiCdp9AAbAvE7a5rFcJf86YR73QX08K8BX7OMzkn3DsqdnWvLB3l3W
   134  6kvIuP+75SrMNeYAcU8PI1+bzLcAG3VN3jA78zeKALgynUNH50mxuiiU3DO4DZ+p
   135  5QIDAQAB
   136  -----END PUBLIC KEY-----
   137  EOF"`)
   138  
   139  	c.MustSSH(m, "sudo mount --bind /etc/coreos/update-payload-key.pub.pem /usr/share/update_engine/update-payload-key.pub.pem")
   140  
   141  	// disable reboot so the test has explicit control
   142  	c.MustSSH(m, "sudo systemctl mask --now locksmithd.service")
   143  	c.MustSSH(m, "sudo systemctl reset-failed locksmithd.service")
   144  
   145  	c.MustSSH(m, "sudo systemctl restart update-engine.service")
   146  }
   147  
   148  func updateMachine(c cluster.TestCluster, m platform.Machine) {
   149  	c.Logf("Triggering update_engine")
   150  
   151  	out, stderr, err := m.SSH("update_engine_client -check_for_update")
   152  	if err != nil {
   153  		c.Fatalf("Executing update_engine_client failed: %v: %v: %s", out, err, stderr)
   154  	}
   155  
   156  	err = util.WaitUntilReady(120*time.Second, 10*time.Second, func() (bool, error) {
   157  		envs, stderr, err := m.SSH("update_engine_client -status 2>/dev/null")
   158  		if err != nil {
   159  			return false, fmt.Errorf("checking status failed: %v: %s", err, stderr)
   160  		}
   161  
   162  		return splitNewlineEnv(string(envs))["CURRENT_OP"] == "UPDATE_STATUS_UPDATED_NEED_REBOOT", nil
   163  	})
   164  	if err != nil {
   165  		c.Fatalf("waiting for UPDATE_STATUS_UPDATED_NEED_REBOOT: %v", err)
   166  	}
   167  
   168  	c.Logf("Rebooting test machine")
   169  
   170  	if err = m.Reboot(); err != nil {
   171  		c.Fatalf("reboot failed: %v", err)
   172  	}
   173  }
   174  
   175  // splits newline-delimited KEY=VAL pairs into a map
   176  func splitNewlineEnv(envs string) map[string]string {
   177  	m := make(map[string]string)
   178  	sc := bufio.NewScanner(strings.NewReader(envs))
   179  	for sc.Scan() {
   180  		spl := strings.SplitN(sc.Text(), "=", 2)
   181  		m[spl[0]] = spl[1]
   182  	}
   183  	return m
   184  }