github.com/coreos/mantle@v0.13.0/kola/tests/rpmostree/deployments.go (about) 1 // Copyright 2018 Red Hat, 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 rpmostree 16 17 import ( 18 "reflect" 19 "regexp" 20 21 "github.com/coreos/mantle/kola/cluster" 22 "github.com/coreos/mantle/kola/register" 23 "github.com/coreos/mantle/kola/tests/util" 24 "github.com/coreos/mantle/platform/conf" 25 ) 26 27 func init() { 28 register.Register(®ister.Test{ 29 Run: rpmOstreeUpgradeRollback, 30 ClusterSize: 1, 31 Name: "rpmostree.upgrade-rollback", 32 Distros: []string{"fcos", "rhcos"}, 33 FailFast: true, 34 }) 35 register.Register(®ister.Test{ 36 Run: rpmOstreeInstallUninstall, 37 ClusterSize: 1, 38 Name: "rpmostree.install-uninstall", 39 // this Ignition config lands the EPEL repo + key 40 UserData: conf.Ignition(`{ 41 "ignition": { 42 "version": "2.2.0" 43 }, 44 "storage": { 45 "files": [ 46 { 47 "filesystem": "root", 48 "group": { 49 "name": "root" 50 }, 51 "path": "/etc/yum.repos.d/epel.repo", 52 "user": { 53 "name": "root" 54 }, 55 "contents": { 56 "source": "data:,%5Bepel%5D%0Aname%3DExtra%20Packages%20for%20Enterprise%20Linux%207%20-%20%24basearch%0Ametalink%3Dhttps%3A%2F%2Fmirrors.fedoraproject.org%2Fmetalink%3Frepo%3Depel-7%26arch%3D%24basearch%0Afailovermethod%3Dpriority%0Aenabled%3D1%0Agpgcheck%3D1%0Agpgkey%3Dfile%3A%2F%2F%2Fetc%2Fpki%2Frpm-gpg%2FRPM-GPG-KEY-EPEL-7%0A" 57 }, 58 "mode": 420 59 }, 60 { 61 "filesystem": "root", 62 "group": { 63 "name": "root" 64 }, 65 "path": "/etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7", 66 "user": { 67 "name": "root" 68 }, 69 "contents": { 70 "source": "data:,-----BEGIN%20PGP%20PUBLIC%20KEY%20BLOCK-----%0AVersion%3A%20GnuPG%20v1.4.11%20(GNU%2FLinux)%0A%0AmQINBFKuaIQBEAC1UphXwMqCAarPUH%2FZsOFslabeTVO2pDk5YnO96f%2BrgZB7xArB%0AOSeQk7B90iqSJ85%2Fc72OAn4OXYvT63gfCeXpJs5M7emXkPsNQWWSju99lW%2BAqSNm%0AjYWhmRlLRGl0OO7gIwj776dIXvcMNFlzSPj00N2xAqjMbjlnV2n2abAE5gq6VpqP%0AvFXVyfrVa%2FualogDVmf6h2t4Rdpifq8qTHsHFU3xpCz%2BT6%2FdGWKGQ42ZQfTaLnDM%0AjToAsmY0AyevkIbX6iZVtzGvanYpPcWW4X0RDPcpqfFNZk643xI4lsZ%2BY2Er9Yu5%0AS%2F8x0ly%2BtmmIokaE0wwbdUu740YTZjCesroYWiRg5zuQ2xfKxJoV5E%2BEh%2BtYwGDJ%0An6HfWhRgnudRRwvuJ45ztYVtKulKw8QQpd2STWrcQQDJaRWmnMooX%2FPATTjCBExB%0A9dkz38Druvk7IkHMtsIqlkAOQMdsX1d3Tov6BE2XDjIG0zFxLduJGbVwc%2F6rIc95%0AT055j36Ez0HrjxdpTGOOHxRqMK5m9flFbaxxtDnS7w77WqzW7HjFrD0VeTx2vnjj%0AGqchHEQpfDpFOzb8LTFhgYidyRNUflQY35WLOzLNV%2BpV3eQ3Jg11UFwelSNLqfQf%0AuFRGc%2BzcwkNjHh5yPvm9odR1BIfqJ6sKGPGbtPNXo7ERMRypWyRz0zi0twARAQAB%0AtChGZWRvcmEgRVBFTCAoNykgPGVwZWxAZmVkb3JhcHJvamVjdC5vcmc%2BiQI4BBMB%0AAgAiBQJSrmiEAhsPBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRBqL66iNSxk%0A5cfGD%2F4spqpsTjtDM7qpytKLHKruZtvuWiqt5RfvT9ww9GUUFMZ4ZZGX4nUXg49q%0AixDLayWR8ddG%2Fs5kyOi3C0uX%2F6inzaYyRg%2BBh70brqKUK14F1BrrPi29eaKfG%2BGu%0AMFtXdBG2a7OtPmw3yuKmq9Epv6B0mP6E5KSdvSRSqJWtGcA6wRS%2FwDzXJENHp5re%0A9Ism3CYydpy0GLRA5wo4fPB5uLdUhLEUDvh2KK%2F%2FfMjja3o0L%2BSNz8N0aDZyn5Ax%0ACU9RB3EHcTecFgoy5umRj99BZrebR1NO%2B4gBrivIfdvD4fJNfNBHXwhSH9ACGCNv%0AHnXVjHQF9iHWApKkRIeh8Fr2n5dtfJEF7SEX8GbX7FbsWo29kXMrVgNqHNyDnfAB%0AVoPubgQdtJZJkVZAkaHrMu8AytwT62Q4eNqmJI1aWbZQNI5jWYqc6RKuCK6%2FF99q%0AthFT9gJO17%2ByRuL6Uv2%2FvgzVR1RGdwVLKwlUjGPAjYflpCQwWMAASxiv9uPyYPHc%0AErSrbRG0wjIfAR3vus1OSOx3xZHZpXFfmQTsDP7zVROLzV98R3JwFAxJ4%2FxqeON4%0AvCPFU6OsT3lWQ8w7il5ohY95wmujfr6lk89kEzJdOTzcn7DBbUru33CQMGKZ3Evt%0ARjsC7FDbL017qxS%2BZVA%2FHGkyfiu4cpgV8VUnbql5eAZ%2B1Ll6Dw%3D%3D%0A%3DhdPa%0A-----END%20PGP%20PUBLIC%20KEY%20BLOCK-----%0A" 71 }, 72 "mode": 420 73 } 74 ] 75 } 76 }`), 77 UserDataV3: conf.Ignition(`{ 78 "ignition": { 79 "version": "3.0.0" 80 }, 81 "storage": { 82 "files": [ 83 { 84 "group": { 85 "name": "root" 86 }, 87 "path": "/etc/yum.repos.d/epel.repo", 88 "user": { 89 "name": "root" 90 }, 91 "contents": { 92 "source": "data:,%5Bepel%5D%0Aname%3DExtra%20Packages%20for%20Enterprise%20Linux%207%20-%20%24basearch%0Ametalink%3Dhttps%3A%2F%2Fmirrors.fedoraproject.org%2Fmetalink%3Frepo%3Depel-7%26arch%3D%24basearch%0Afailovermethod%3Dpriority%0Aenabled%3D1%0Agpgcheck%3D1%0Agpgkey%3Dfile%3A%2F%2F%2Fetc%2Fpki%2Frpm-gpg%2FRPM-GPG-KEY-EPEL-7%0A" 93 }, 94 "mode": 420 95 }, 96 { 97 "group": { 98 "name": "root" 99 }, 100 "path": "/etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7", 101 "user": { 102 "name": "root" 103 }, 104 "contents": { 105 "source": "data:,-----BEGIN%20PGP%20PUBLIC%20KEY%20BLOCK-----%0AVersion%3A%20GnuPG%20v1.4.11%20(GNU%2FLinux)%0A%0AmQINBFKuaIQBEAC1UphXwMqCAarPUH%2FZsOFslabeTVO2pDk5YnO96f%2BrgZB7xArB%0AOSeQk7B90iqSJ85%2Fc72OAn4OXYvT63gfCeXpJs5M7emXkPsNQWWSju99lW%2BAqSNm%0AjYWhmRlLRGl0OO7gIwj776dIXvcMNFlzSPj00N2xAqjMbjlnV2n2abAE5gq6VpqP%0AvFXVyfrVa%2FualogDVmf6h2t4Rdpifq8qTHsHFU3xpCz%2BT6%2FdGWKGQ42ZQfTaLnDM%0AjToAsmY0AyevkIbX6iZVtzGvanYpPcWW4X0RDPcpqfFNZk643xI4lsZ%2BY2Er9Yu5%0AS%2F8x0ly%2BtmmIokaE0wwbdUu740YTZjCesroYWiRg5zuQ2xfKxJoV5E%2BEh%2BtYwGDJ%0An6HfWhRgnudRRwvuJ45ztYVtKulKw8QQpd2STWrcQQDJaRWmnMooX%2FPATTjCBExB%0A9dkz38Druvk7IkHMtsIqlkAOQMdsX1d3Tov6BE2XDjIG0zFxLduJGbVwc%2F6rIc95%0AT055j36Ez0HrjxdpTGOOHxRqMK5m9flFbaxxtDnS7w77WqzW7HjFrD0VeTx2vnjj%0AGqchHEQpfDpFOzb8LTFhgYidyRNUflQY35WLOzLNV%2BpV3eQ3Jg11UFwelSNLqfQf%0AuFRGc%2BzcwkNjHh5yPvm9odR1BIfqJ6sKGPGbtPNXo7ERMRypWyRz0zi0twARAQAB%0AtChGZWRvcmEgRVBFTCAoNykgPGVwZWxAZmVkb3JhcHJvamVjdC5vcmc%2BiQI4BBMB%0AAgAiBQJSrmiEAhsPBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRBqL66iNSxk%0A5cfGD%2F4spqpsTjtDM7qpytKLHKruZtvuWiqt5RfvT9ww9GUUFMZ4ZZGX4nUXg49q%0AixDLayWR8ddG%2Fs5kyOi3C0uX%2F6inzaYyRg%2BBh70brqKUK14F1BrrPi29eaKfG%2BGu%0AMFtXdBG2a7OtPmw3yuKmq9Epv6B0mP6E5KSdvSRSqJWtGcA6wRS%2FwDzXJENHp5re%0A9Ism3CYydpy0GLRA5wo4fPB5uLdUhLEUDvh2KK%2F%2FfMjja3o0L%2BSNz8N0aDZyn5Ax%0ACU9RB3EHcTecFgoy5umRj99BZrebR1NO%2B4gBrivIfdvD4fJNfNBHXwhSH9ACGCNv%0AHnXVjHQF9iHWApKkRIeh8Fr2n5dtfJEF7SEX8GbX7FbsWo29kXMrVgNqHNyDnfAB%0AVoPubgQdtJZJkVZAkaHrMu8AytwT62Q4eNqmJI1aWbZQNI5jWYqc6RKuCK6%2FF99q%0AthFT9gJO17%2ByRuL6Uv2%2FvgzVR1RGdwVLKwlUjGPAjYflpCQwWMAASxiv9uPyYPHc%0AErSrbRG0wjIfAR3vus1OSOx3xZHZpXFfmQTsDP7zVROLzV98R3JwFAxJ4%2FxqeON4%0AvCPFU6OsT3lWQ8w7il5ohY95wmujfr6lk89kEzJdOTzcn7DBbUru33CQMGKZ3Evt%0ARjsC7FDbL017qxS%2BZVA%2FHGkyfiu4cpgV8VUnbql5eAZ%2B1Ll6Dw%3D%3D%0A%3DhdPa%0A-----END%20PGP%20PUBLIC%20KEY%20BLOCK-----%0A" 106 }, 107 "mode": 420 108 } 109 ] 110 } 111 }`), 112 113 Distros: []string{"fcos", "rhcos"}, 114 Flags: []register.Flag{register.RequiresInternetAccess}, // these need network to retrieve bits 115 }) 116 } 117 118 // rpmOstreeUpgradeRollback simulates an upgrade by creating a local branch, making 119 // a commit to the branch, and rebases the host to said commit. After a successful 120 // "upgrade", the host is rolled back to the original deployment. 121 func rpmOstreeUpgradeRollback(c cluster.TestCluster) { 122 var newBranch string = "local-branch" 123 var newVersion string = "kola-test-1.0" 124 125 m := c.Machines()[0] 126 127 originalStatus, err := util.GetRpmOstreeStatusJSON(c, m) 128 if err != nil { 129 c.Fatal(err) 130 } 131 132 if len(originalStatus.Deployments) < 1 { 133 c.Fatalf(`Unexpected results from "rpm-ostree status"; received: %v`, originalStatus) 134 } 135 136 c.Run("upgrade", func(c cluster.TestCluster) { 137 // create a local branch to act as our upgrade target 138 originalCsum := originalStatus.Deployments[0].Checksum 139 createBranch := "sudo ostree refs --create " + newBranch + " " + originalCsum 140 c.MustSSH(m, createBranch) 141 142 // make a commit to the new branch 143 createCommit := "sudo ostree commit -b " + newBranch + " --tree ref=" + originalCsum + " --add-metadata-string version=" + newVersion 144 newCommit := c.MustSSH(m, createCommit) 145 146 // use "rpm-ostree rebase" to get to the "new" commit 147 c.MustSSH(m, "sudo rpm-ostree rebase :"+newBranch) 148 149 // get latest rpm-ostree status output to check validity 150 postUpgradeStatus, err := util.GetRpmOstreeStatusJSON(c, m) 151 if err != nil { 152 c.Fatal(err) 153 } 154 155 // should have an additional deployment 156 if len(postUpgradeStatus.Deployments) != len(originalStatus.Deployments)+1 { 157 c.Fatalf("Expected %d deployments; found %d deployments", len(originalStatus.Deployments)+1, len(postUpgradeStatus.Deployments)) 158 } 159 160 // reboot into new deployment 161 rebootErr := m.Reboot() 162 if rebootErr != nil { 163 c.Fatalf("Failed to reboot machine: %v", err) 164 } 165 166 // get latest rpm-ostree status output 167 postRebootStatus, err := util.GetRpmOstreeStatusJSON(c, m) 168 if err != nil { 169 c.Fatal(err) 170 } 171 172 // should have 2 deployments, the previously booted deployment and the test deployment due to rpm-ostree pruning 173 if len(postRebootStatus.Deployments) != 2 { 174 c.Fatalf("Expected %d deployments; found %d deployment", 2, len(postRebootStatus.Deployments)) 175 } 176 177 // origin should be new branch 178 if postRebootStatus.Deployments[0].Origin != newBranch { 179 c.Fatalf(`New deployment origin is incorrect; expected %q, got %q`, newBranch, postRebootStatus.Deployments[0].Origin) 180 } 181 182 // new deployment should be booted 183 if !postRebootStatus.Deployments[0].Booted { 184 c.Fatalf("New deployment is not reporting as booted") 185 } 186 187 // checksum should be new commit 188 if postRebootStatus.Deployments[0].Checksum != string(newCommit) { 189 c.Fatalf(`New deployment checksum is incorrect; expected %q, got %q`, newCommit, postRebootStatus.Deployments[0].Checksum) 190 } 191 192 // version should be new version string 193 if postRebootStatus.Deployments[0].Version != newVersion { 194 c.Fatalf(`New deployment version is incorrect; expected %q, got %q`, newVersion, postRebootStatus.Deployments[0].Checksum) 195 } 196 }) 197 198 c.Run("rollback", func(c cluster.TestCluster) { 199 // rollback to original deployment 200 c.MustSSH(m, "sudo rpm-ostree rollback") 201 202 newRebootErr := m.Reboot() 203 if newRebootErr != nil { 204 c.Fatalf("Failed to reboot machine: %v", err) 205 } 206 207 rollbackStatus, err := util.GetRpmOstreeStatusJSON(c, m) 208 if err != nil { 209 c.Fatal(err) 210 } 211 212 // still 2 deployments... 213 if len(rollbackStatus.Deployments) != 2 { 214 c.Fatalf("Expected %d deployments; found %d deployments", 2, len(rollbackStatus.Deployments)) 215 } 216 217 // validate we are back to the original deployment by comparing the 218 // the two rpmOstreeDeployment structs 219 if !reflect.DeepEqual(originalStatus.Deployments[0], rollbackStatus.Deployments[0]) { 220 c.Fatalf(`Differences found in "rpm-ostree status"; original %v, current: %v`, originalStatus.Deployments[0], rollbackStatus.Deployments[0]) 221 } 222 223 // cleanup our mess 224 cleanupErr := rpmOstreeCleanup(c, m) 225 if cleanupErr != nil { 226 c.Fatal(cleanupErr) 227 } 228 }) 229 } 230 231 // rpmOstreeInstallUninstall verifies that we can install a package 232 // and then uninstall it 233 // 234 // 'bcrypt' is available in EPEL and installs on Fedora 29 and RHEL 8 235 // 236 // NOTE: we could be churning on the package choice going forward as 237 // we need something that is a) small, b) has no dependencies, and c) 238 // can be installed on Fedora + RHEL from the EPEL repo that we are 239 // currently using. We've already had to swap from `fpaste` to `bcrypt` 240 func rpmOstreeInstallUninstall(c cluster.TestCluster) { 241 var installPkgName = "bcrypt" 242 var installPkgBin = "/bin/bcrypt" 243 244 m := c.Machines()[0] 245 246 originalStatus, err := util.GetRpmOstreeStatusJSON(c, m) 247 if err != nil { 248 c.Fatal(err) 249 } 250 251 if len(originalStatus.Deployments) < 1 { 252 c.Fatal(`Unexpected results from "rpm-ostree status"; no deployments?`) 253 } 254 255 originalCsum := originalStatus.Deployments[0].Checksum 256 257 c.Run("install", func(c cluster.TestCluster) { 258 // install package and reboot 259 c.MustSSH(m, "sudo rpm-ostree install "+installPkgName) 260 261 installRebootErr := m.Reboot() 262 if installRebootErr != nil { 263 c.Fatalf("Failed to reboot machine: %v", installRebootErr) 264 } 265 266 postInstallStatus, err := util.GetRpmOstreeStatusJSON(c, m) 267 if err != nil { 268 c.Fatal(err) 269 } 270 271 if len(postInstallStatus.Deployments) != 2 { 272 c.Fatalf(`Expected two deployments, found %d deployments`, len(postInstallStatus.Deployments)) 273 } 274 275 // check the command is present, in the rpmdb, and usable 276 cmdOut := c.MustSSH(m, "command -v "+installPkgName) 277 if string(cmdOut) != installPkgBin { 278 c.Fatalf(`%q binary in unexpected location. expectd %q, got %q`, installPkgName, installPkgBin, string(cmdOut)) 279 } 280 281 rpmOut := c.MustSSH(m, "rpm -q "+installPkgName) 282 // forcing use of x86_64; may need to adapt this in the future 283 rpmRegex := "^" + installPkgName + ".*x86_64" 284 rpmMatch := regexp.MustCompile(rpmRegex).MatchString(string(rpmOut)) 285 if !rpmMatch { 286 c.Fatalf(`Output from "rpm -q" was unexpected: %q`, string(rpmOut)) 287 } 288 289 // package should be in the metadata 290 var reqPkg bool = false 291 for _, pkg := range postInstallStatus.Deployments[0].RequestedPackages { 292 if pkg == installPkgName { 293 reqPkg = true 294 break 295 } 296 } 297 if !reqPkg { 298 c.Fatalf(`Unable to find "%q" in requested-packages: %v`, installPkgName, postInstallStatus.Deployments[0].RequestedPackages) 299 } 300 301 var installPkg bool = false 302 for _, pkg := range postInstallStatus.Deployments[0].Packages { 303 if pkg == installPkgName { 304 installPkg = true 305 break 306 } 307 } 308 if !installPkg { 309 c.Fatalf(`Unable to find "%q" in packages: %v`, installPkgName, postInstallStatus.Deployments[0].Packages) 310 } 311 312 // checksum should be different 313 if postInstallStatus.Deployments[0].Checksum == originalCsum { 314 c.Fatalf(`Commit IDs incorrectly matched after package install`) 315 } 316 }) 317 318 // uninstall the package 319 c.Run("uninstall", func(c cluster.TestCluster) { 320 c.MustSSH(m, "sudo rpm-ostree uninstall "+installPkgName) 321 322 uninstallRebootErr := m.Reboot() 323 if uninstallRebootErr != nil { 324 c.Fatalf("Failed to reboot machine: %v", uninstallRebootErr) 325 } 326 327 postUninstallStatus, err := util.GetRpmOstreeStatusJSON(c, m) 328 if err != nil { 329 c.Fatal(err) 330 } 331 332 // check the metadata to make sure everything went well 333 if len(postUninstallStatus.Deployments) != 2 { 334 c.Fatal("Expected %d deployments, got %d", 2, len(postUninstallStatus.Deployments)) 335 } 336 337 if postUninstallStatus.Deployments[0].Checksum != originalCsum { 338 c.Fatalf(`Checksum is incorrect; expected %q, got %q`, originalCsum, postUninstallStatus.Deployments[0].Checksum) 339 } 340 341 if len(postUninstallStatus.Deployments[0].RequestedPackages) != 0 { 342 c.Fatalf(`Found unexpected requested-packages: %q`, postUninstallStatus.Deployments[0].RequestedPackages) 343 } 344 345 if len(postUninstallStatus.Deployments[0].Packages) != 0 { 346 c.Fatalf(`Found unexpected packages: %q`, postUninstallStatus.Deployments[0].Packages) 347 } 348 349 // cleanup our mess 350 cleanupErr := rpmOstreeCleanup(c, m) 351 if cleanupErr != nil { 352 c.Fatal(cleanupErr) 353 } 354 }) 355 }