User Tools

Site Tools


eve-kvm:upgrade-eve-terraform

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

eve-kvm:upgrade-eve-terraform [2026/09/28 19:33] – created mceve-kvm:upgrade-eve-terraform [2026/09/28 19:52] (current) – mc
Line 4: Line 4:
 ''zededa/zedcloud'' Terraform provider. ''zededa/zedcloud'' Terraform provider.
  
-You do **not** need to reinstall the node, touch a USB stick, or visit the site. An EVE-OS +There is no need to reinstall the node, use a USB stick, or visit the site. An EVE-OS 
-upgrade is a file download plus a reboot, and both are driven from the controller.+upgrade is a file download plus a reboot, both driven from the controller.
  
-**Time needed:** about 10 minutes of typing, plus 5-20 minutes of waiting while the node +**Time needed:** roughly 10 minutes of editing, plus 5-20 minutes while the node downloads 
-downloads and reboots.+and reboots.
  
 ---- ----
  
-===== How it works, in plain words =====+===== How it works =====
  
-Every edge node has **two** root partitions, called ''IMGA'' and ''IMGB''. Only one is +Every edge node has two root partitions, ''IMGA'' and ''IMGB''. One is active, the other is 
-active at a time. The other is spare.+spare.
  
-  - You tell the controller "this node should run version X". +  - You tell the controller which version the node should run. 
-  - The node downloads version X into whichever partition is //not// currently in use.+  - The node downloads that version into whichever partition is //not// currently in use.
   - The node reboots into that partition.   - The node reboots into that partition.
-  - If the new version does not check in with the controller, the node **automatically +  - If the new version does not check in with the controller, the node falls back to the 
-    falls back** to the old partition.+    old partition automatically.
  
-That last point is why this is safe: a bad upgrade costs you a reboot, not a site visit.+Because of that last step, an upgrade that goes wrong generally costs a reboot rather than 
 +a site visit.
  
-To make this happen in Terraform you need **two** things:+Two pieces of Terraform are involved:
  
-^ Thing ^ Terraform ^ What it is ^ +^ Piece ^ Resource ^ Purpose ^ 
-| An **image record** | ''zedcloud_image'' with ''image_type = "IMAGE_TYPE_EVE"'' | A pointer telling the controller //where the rootfs file lives// and //what its checksum is// | +| Image record, in the image repository | ''zedcloud_image'' with ''image_type = "IMAGE_TYPE_EVE"'' | Tells the controller where the rootfs file lives and what its checksum is | 
-| A **node setting** | a ''base_image'' block inside your ''zedcloud_edgenode'' | Says "this node should run that image" |+| Node setting | a ''base_image'' block inside your ''zedcloud_edgenode'' | Tells the node to run that image |
  
-The image record is the part people forget. Keep reading.+The order matters: the image record comes first, the ''base_image'' reference second.
  
 ---- ----
  
-===== !! Read this before you start !! =====+===== The image must already exist in the image repository =====
  
-**An EVE version name is not magic text.** You cannot simply type ''16.0.2-lts-kvm-amd64'' +**''base_image'' does not accept an arbitrary version string.** It is a reference to an 
-into ''base_image'' and expect it to work. That name has to already exist as an image +image record that already exists in your enterprise's image repository. A node cannot be 
-record in your enterprise, //and that record has to be complete//.+told to run a version the repository has never heard of, so the image record has to be 
 +created and applied //before// any ''base_image'' block points at it.
  
-There is no built-in catalogue of EVE releases that fills this in for you. If you have +Nothing populates the repository on your behalf. There is no hidden catalogue of EVE 
-never created an EVE image record before, you have zero of them, no matter how many +releases, so every version you intend to deploy needs its own ''zedcloud_image'' resource. 
-versions ZEDEDA has published.+That is what Steps 2 to 4 are for; Step 5 only works once Step 4 has been applied.
  
-There are two ways this bites you, and the second one is nasty:+To list the EVE images your repository currently holds:
  
-^ What you did ^ What happens ^ How obvious is it ^ +<code bash> 
-| Named a version with **no image record at all** | The controller cannot resolve the name to an image ID, so the apply fails | Obvious. Terraform shows an error. | +TOK=$(grep -E '^zedcloud_token' secret.auto.tfvars | sed -E 's/^[^=]*= *"?([^"]*)"?.*/\1/') 
-| Named a version whose image record exists but is **missing its SHA-256** | ''terraform apply'' says **Apply complete!** and the node then fails on its own | **Not obvious at all.** Terraform is green and the node is broken. |+CTRL=https://zedcontrol.gmwtus.zededa.net
  
-The second case looks like this on the node:+curl -s -H "Authorization: Bearer $TOK" \ 
 +  "$CTRL/api/v1/apps/images?imageType=IMAGE_TYPE_EVE&next.pageSize=200" \ 
 +| python3 -c " 
 +import json,sys 
 +rows = json.load(sys.stdin).get('list',[]) 
 +print('%d EVE image(s) in the repository' % len(rows)) 
 +for i in rows: 
 +    print(' ', i['imageArch'], i['imageStatus'], i['name'], 
 +          '| sha:', (i.get('imageSha256') or '(NONE)')[:16]) 
 +" 
 +</code> 
 + 
 +An empty list is normal for an enterprise that has not deployed an EVE image before. 
 +Whatever you put in ''base_image.image_name'' has to appear in this list, with a SHA-256, 
 +before the node can act on it. 
 + 
 +Two things can go wrong, and they look quite different: 
 + 
 +^ Situation ^ Result ^ 
 +| The named version is not in the repository at all | The controller cannot resolve the name to an image ID, and the apply fails with an error | 
 +| The record is in the repository but has no SHA-256 | The apply succeeds, and the node then reports a failure on its own | 
 + 
 +The second case shows up on the node like this:
  
 <code> <code>
Line 59: Line 83:
 </code> </code>
  
-The node is fine, it is still running the old version, but the upgrade never started. +The node is unharmed and still running the old version, but the upgrade never started. 
- +This is why Step 7 checks the node rather than relying on Terraform's output.
-**The lesson:** a green Terraform apply does not mean the node upgraded. Always do Step 7.+
  
 ---- ----
  
-===== Step 1: Write down what is running now =====+===== Step 1: Record what is running now =====
  
-Do this first so you can tell later whether anything changed.+Useful to have for comparison afterwards, and if you ever want to go back.
  
 <code bash> <code bash>
Line 73: Line 96:
 TOK=$(grep -E '^zedcloud_token' secret.auto.tfvars | sed -E 's/^[^=]*= *"?([^"]*)"?.*/\1/') TOK=$(grep -E '^zedcloud_token' secret.auto.tfvars | sed -E 's/^[^=]*= *"?([^"]*)"?.*/\1/')
  
-# Change this to your controller and your node name+# Adjust to your controller and node name
 CTRL=https://zedcontrol.gmwtus.zededa.net CTRL=https://zedcontrol.gmwtus.zededa.net
 NODE=TF-DEMO-AI-PX-1 NODE=TF-DEMO-AI-PX-1
Line 85: Line 108:
 </code> </code>
  
-Healthy output looks like this. One partition ''active'' with a version, the other ''unused'':+A settled node shows one partition ''active'' with a version and the other ''unused'':
  
 <code> <code>
Line 92: Line 115:
 </code> </code>
  
-The string in quotes is the **exact** version name. Copy it somewhere. You will want it if +The string in quotes is the exact version name. Keep a copy.
-you ever need to go back.+
  
 ---- ----
Line 99: Line 121:
 ===== Step 2: Put the rootfs file where the nodes can reach it ===== ===== Step 2: Put the rootfs file where the nodes can reach it =====
  
-You need the ''*.rootfs.img'' file on a datastore. **The node downloads it itself** - the +The ''*.rootfs.img'' file needs to be on a datastore. The node downloads it itself - the 
-controller does not push it. So "reachable" means reachable //from the edge node//, not +controller does not push it out - so it needs to be reachable from the edge node rather 
-from your laptop.+than from your workstation.
  
-Any of these work: HTTP, HTTPS, AWS S3, Azure Blob, SFTP.+HTTP, HTTPS, AWS S3, Azure Blob and SFTP all work. A plain HTTP file server is the simplest 
 +option. In this project that is the datastore ''TF-DEMO-AI-ATL-DS'' at 
 +''http://192.168.0.101:1080''.
  
-A plain HTTP file server is the easiest. In this project that is the datastore +Once the file is in place, confirm it:
-''TF-DEMO-AI-ATL-DS'', which is ''http://192.168.0.101:1080''. +
- +
-Drop the file there, then prove it is really there:+
  
 <code bash> <code bash>
Line 114: Line 135:
 </code> </code>
  
-You want ''HTTP/1.1 200 OK'' and a sensible ''Content-Length'' (a rootfs is a few hundred +Look for ''HTTP/1.1 200 OK'' and a plausible ''Content-Length'' - a rootfs is a few hundred 
-MB). Write the ''Content-Length'' number down, you will use it in Step 4.+MB. Note the ''Content-Length'', it is used in Step 4.
  
 <code> <code>
Line 122: Line 143:
 </code> </code>
  
-//If your datastore is on a private LAN, remember the nodes must be on that LAN or routed +//If the datastore is on a private LAN, the nodes need to be on that LAN or routed to it. 
-to it. An S3 bucket is the safer choice for nodes spread across sites.//+For nodes spread across several sites, S3 or similar tends to be easier.//
  
 ---- ----
Line 129: Line 150:
 ===== Step 3: Get the SHA-256 checksum ===== ===== Step 3: Get the SHA-256 checksum =====
  
-The node verifies the checksum before it installs anything. Get this wrong and the node +The node verifies the checksum before installing, so it needs to be right.
-refuses the file.+
  
-Whoever built the image should give you the hash. **Verify it yourself anyway** - it takes +Whoever built the image will usually supply the hash. Checking it against the file takes a 
-seconds and it is the single most common cause of a failed upgrade:+couple of seconds and rules out a truncated or corrupted upload:
  
 <code bash> <code bash>
Line 139: Line 159:
   | shasum -a 256   | shasum -a 256
 </code> </code>
- 
-Output: 
  
 <code> <code>
Line 146: Line 164:
 </code> </code>
  
-That must match what you were given, character for character. It must be 64 characters +It should match the hash you were given exactly, and be 64 characters long. If it does not 
-long. If it does not match, **stop** - you have the wrong file or a corrupted upload.+match, the file on the datastore is not the file you think it is - worth resolving before 
 +going further.
  
 ---- ----
  
-===== Step 4: Add the image record to your Terraform =====+===== Step 4: Add the image to the repository =====
  
-Add a new ''zedcloud_image'' resource. Put it next to your other image resources. In this +This is the step that puts the version into the image repository, so that Step 5 has 
-project that is ''1-Infra-ai.tf''.+something to reference.
  
-This is an **addition**. Do not edit or delete your existing image resources.+Add a ''zedcloud_image'' resource alongside your existing image resources. In this project 
 +those live in ''1-Infra-ai.tf''. 
 + 
 +This is a new resource; your existing ones stay as they are.
  
 <code> <code>
Line 173: Line 195:
 </code> </code>
  
-What each field means: +^ Field ^ Value ^
- +
-^ Field ^ What to put ^+
 | ''datastore_id'' | Reference to the datastore from Step 2 | | ''datastore_id'' | Reference to the datastore from Step 2 |
-| ''image_type'' | Always ''IMAGE_TYPE_EVE'' for a base OS. //Not// ''IMAGE_TYPE_APPLICATION''. | +| ''image_type'' | ''IMAGE_TYPE_EVE'' for a base OS, as opposed to ''IMAGE_TYPE_APPLICATION'' | 
-| ''image_arch'' | ''AMD64'' for Intel/AMD boxes, ''ARM64'' for Jetson and other ARM | +| ''image_arch'' | ''AMD64'' for Intel/AMD, ''ARM64'' for Jetson and other ARM boards | 
-| ''image_format'' | Always ''RAW'' for an EVE rootfs | +| ''image_format'' | ''RAW'' for an EVE rootfs | 
-| ''name'' | The EVE version string, **without** the ''.rootfs.img'' suffix | +| ''name'' | The EVE version string, without the ''.rootfs.img'' suffix | 
-| ''image_rel_url'' | The filename, relative to the datastore root. **With** the suffix. |+| ''image_rel_url'' | The filename relative to the datastore root, with the suffix |
 | ''image_sha256'' | The 64-character hash from Step 3 | | ''image_sha256'' | The 64-character hash from Step 3 |
 | ''image_size_bytes'' | The ''Content-Length'' from Step 2 | | ''image_size_bytes'' | The ''Content-Length'' from Step 2 |
  
-**Do not leave ''image_sha256'' empty as a placeholder.** Terraform will happily create the +''image_sha256'' is worth filling in before you apply. Terraform will create the record 
-record and every node pointed at it will fail with ''no content sha256 defined''.+without it, and nodes pointed at that record then fail with ''no content sha256 defined''.
  
 ---- ----
Line 192: Line 212:
 ===== Step 5: Point the node at the image ===== ===== Step 5: Point the node at the image =====
  
-Add a ''base_image'' block inside the ''zedcloud_edgenode'' resource you want to upgrade. +This step only works for an image that is in the repository. If you skipped Step 4, or its 
-In this project that is ''3-EdgeNodes-ai.tf''.+record has no SHA-256, come back once that is sorted - the repository listing command above 
 +is the quickest way to confirm.
  
-Again, an **addition**. Leave the rest of the node resource alone.+Add a ''base_image'' block inside the ''zedcloud_edgenode'' resource for the node you are 
 +upgrading. In this project the nodes are in ''3-EdgeNodes-ai.tf''. 
 + 
 +Another addition - the rest of the node resource is unchanged.
  
 <code> <code>
 resource "zedcloud_edgenode" "demo_en_px" { resource "zedcloud_edgenode" "demo_en_px" {
-  # ... all your existing settings stay exactly as they are ...+  # ... existing settings unchanged ...
  
   base_image {   base_image {
Line 209: Line 233:
 </code> </code>
  
-^ Field ^ What to put ^ +^ Field ^ Value ^ 
-| ''image_name'' | Reference your Step 4 resource with ''.name''. Do not retype the string - let Terraform link them so it creates the image before it uses it. |+| ''image_name'' | Reference to your Step 4 resource via ''.name''. Referencing rather than retyping gives Terraform the dependency, so it creates the image before the node needs it. |
 | ''version'' | The same version string | | ''version'' | The same version string |
-| ''activate'' | ''true'' means go now. ''false'' stages the setting without starting the upgrade. |+| ''activate'' | ''true'' starts the upgrade. ''false'' stages the setting without starting it. |
  
-Naming a new image here **is** the upgrade. There is no separate "upgrade" command and no +Naming a new image here is the whole upgrade - there is no separate upgrade command or 
-force flag to set. EVE stages the new version to the spare partition and reboots into it.+force flag. EVE stages the new version to the spare partition and reboots into it.
  
-//If the node uses ''for_each'', one ''base_image'' block covers every instance.//+//A node resource using ''for_each'' needs only one ''base_image'' block; it applies to every 
 +instance.//
  
-Now check your syntax:+Then check the syntax:
  
 <code bash> <code bash>
Line 228: Line 253:
 ---- ----
  
-===== Step 6: Apply, in two commands =====+===== Step 6: Apply =====
  
 ==== 6a. Preview ==== ==== 6a. Preview ====
Line 236: Line 261:
 </code> </code>
  
-Read the output. You are looking for your new image being **created** and your node being +You are looking for the new image being created and the node updated in place:
-**updated in place**:+
  
 <code> <code>
Line 246: Line 270:
 </code> </code>
  
-**If you see anything being //destroyed// that you did not expect, stop and read it.** +Anything listed as being destroyed is worth reading before you continue. On an established 
-This matters a lot on an existing codebase: a plain ''terraform apply'' also picks up every +codebase a plain ''terraform apply'' also picks up unrelated drift accumulated since the 
-unrelated drift that has built up since the last run.+last run.
  
-==== 6b. Apply only what you mean to ====+==== 6b. Apply the upgrade ====
  
-Use ''-target'' so you change the image and the node and **nothing else**:+''-target'' limits the apply to the image and the node:
  
 <code bash> <code bash>
Line 260: Line 284:
 </code> </code>
  
-Expected: ''1 to add, 3 to change'' (the 3 being however many nodes the resource covers), +Expect ''1 to add'' and one change per node covered by the resource, with nothing destroyed.
-and **0 to destroy**.+
  
-Why ''-target'' and not a plain apply:+Two reasons to scope it this way:
  
-  * It cannot trip over unrelated drift elsewhere in your code. +  * Unrelated drift elsewhere in the configuration stays out of the picture. 
-  * It cannot delete an old image record that a node is still using. If you removed an +  * If the same edit removed an older EVE image record from the configuration, Terraform no 
-    older EVE image from your config in the same edit, Terraform has no way to know it must +    longer has a dependency telling it to delete that record //after// repointing the node, 
-    delete that //after// repointing the node, and may try to delete an in-use image +    so it may attempt to delete an image that is still in use.
-    mid-apply.+
  
-==== 6c. Tidy up later ====+==== 6c. Catch up afterwards ====
  
-Once the upgrade has landed and you are happy, run a normal apply to catch up on +Once the upgrade has landed, a normal apply brings everything else into line, including 
-everything else, including deleting any EVE image records you removed from the config:+removing any EVE image records you dropped from the configuration:
  
 <code bash> <code bash>
-terraform plan     # read it properly first+terraform plan
 terraform apply terraform apply
 </code> </code>
Line 283: Line 305:
 ---- ----
  
-===== Step 7: Watch the upgrade. Do not skip this. =====+===== Step 7: Watch the node =====
  
-Terraform saying ''Apply complete!'' only means the controller accepted the setting. It +''Apply complete!'' means the controller accepted the setting. Whether the node succeeded 
-says nothing about whether the node succeeded.+is a separate question, and the node is the place to look.
  
-Run the Step 1 command again, every minute or two:+Re-run the Step 1 query every minute or two:
  
 <code bash> <code bash>
Line 307: Line 329:
 </code> </code>
  
-What you should see, in order:+The sequence to expect:
  
-  - **Downloading.** The spare partition shows the new version and ''dl='' climbing from 0 to 100. +  - **Downloading.** The spare partition shows the new version, with ''dl='' climbing to 100. 
-  - **Rebooting.** The node drops offline for a few minutes. This is normal. +  - **Rebooting.** The node goes offline for a few minutes. 
-  - **Testing.** The spare partition becomes ''active'' with ''partitionState'' of ''testing''. +  - **Testing.** The spare partition becomes ''active'', with ''partitionState'' of ''testing''. 
-  - **Done.** State settles to ''active'' / ''DEVICE_SW_STATUS_UPDATED'', and the partition that +  - **Settled.** State reaches ''active'' / ''DEVICE_SW_STATUS_UPDATED'', and the previously 
-    used to be active now reads ''unused''. The two partitions have swapped roles.+    active partition now reads ''unused''. The two partitions have swapped roles.
  
 ---- ----
  
-===== If it goes wrong =====+===== If it does not go to plan =====
  
-==== The node says DEVICE_SW_STATUS_FAILED ====+==== The node reports DEVICE_SW_STATUS_FAILED ====
  
-Read the ''swError'' text. It is usually specific.+The ''swError'' text is usually specific about the cause.
  
 ^ Error text ^ Cause ^ Fix ^ ^ Error text ^ Cause ^ Fix ^
-| ''no content sha256 defined'' | ''image_sha256'' is empty on the image record | Fill it in (Step 3), apply again | +| ''no content sha256 defined'' | ''image_sha256'' is empty on the image record | Fill it in (Step 3) and apply again | 
-| Checksum / verification mismatch | Hash does not match the actual file | Re-hash the file, correct the record | +| Checksum or verification mismatch | The hash does not match the file | Re-hash the file and correct the record | 
-| Download or 404 errors | Node cannot reach the datastore, or ''image_rel_url'' is wrong | Check the path and the node's network route to the datastore |+| Download or 404 errors | ''image_rel_url'' is wrong, or the node cannot reach the datastore | Check the path, and the node's route to the datastore |
  
-After fixing the record, ask the node to try again:+Once the record is corrected, the node can be asked to try again:
  
 <code bash> <code bash>
-# Needs the node's UUID, not its name+# Uses the node's UUID rather than its name
 curl -s -X PUT -H "Authorization: Bearer $TOK" \ curl -s -X PUT -H "Authorization: Bearer $TOK" \
   "$CTRL/api/v1/devices/id/<NODE-UUID>/baseos/upgrade/retry"   "$CTRL/api/v1/devices/id/<NODE-UUID>/baseos/upgrade/retry"
 </code> </code>
  
-You can also bump ''base_os_retry_counter'' on the ''zedcloud_edgenode'' resource to force a +Bumping ''base_os_retry_counter'' on the ''zedcloud_edgenode'' resource also triggers a retry 
-retry through Terraform.+through Terraform.
  
 ==== The node came back on the old version ==== ==== The node came back on the old version ====
  
-That is the automatic fallback doing its job. The new version booted but did not check in, +This is the automatic fallback: the new version booted but did not check in with the 
-so EVE rolled back. The node is safe. Look at the node's logs or EdgeView for why the new +controller, so EVE returned to the previous partition. The node is in a safe state. The 
-version could not reach the controller.+node's logs or EdgeView will show why the new version could not reach the controller. 
 + 
 +==== Going back deliberately ====
  
-==== I want to go back on purpose ====+Point ''base_image'' at the previous version and apply again - which is what the version 
 +string from Step 1 is for.
  
-Point ''base_image'' at the old version and apply again. This is why you wrote the old +The same repository rule applies in this direction: the older version needs to be in the 
-version string down in Step 1. Note that the old version needs a complete image record +image repository too. If its record was deleted after the upgrade, it has to be recreated 
-too - the same rules apply in both directions.+(Steps 2 to 4) before a node can be sent back to it. The automatic fallback described above 
 +does not depend on this, since that rootfs is already on the node's other partition - but a 
 +deliberate, controller-driven rollback does.
  
 ---- ----
  
-===== Special case: NVIDIA Jetson / Orin =====+===== NVIDIA Jetson and Orin =====
  
-  * Use ''image_arch = "ARM64"''. +  * Set ''image_arch = "ARM64"''. 
-  * You need the Jetson build of EVE, with ''nvidia-jp6'' or similar in the name. A generic +  * Jetson boards need a Jetson build of EVE, identifiable by something like ''nvidia-jp6'' 
-    ARM64 rootfs **will not boot** a Jetson board. +    in the name. A generic ARM64 rootfs will not boot them. 
-  * A rootfs upgrade cannot cross a Jetson bootloader/BSP boundary. If the target version +  * A rootfs upgrade cannot cross a Jetson bootloader/BSP boundary. Where the target 
-    needs a newer BSP than the board has, you need the installer and physical access, not +    version needs a newer BSP than the board currently has, that calls for the installer 
-    this procedure. Check the release notes before you start.+    and physical access rather than this procedure, so the release notes are worth reading 
 +    first.
  
 ---- ----
Line 366: Line 394:
 ===== Quick reference ===== ===== Quick reference =====
  
-^ Step ^ Command ^ +^ Task ^ Command ^ 
-| See current version | ''curl .../devices/name/$NODE/status'' then read ''swInfo'' | +| See current version | ''curl .../devices/name/$NODE/status'', then read ''swInfo'' | 
-| Confirm file is reachable | ''curl -I http://datastore/path/file.rootfs.img'' | +| List EVE images in the repository | ''curl .../apps/images?imageType=IMAGE_TYPE_EVE'' | 
-| Get checksum | ''curl -s http://.../file.rootfs.img \| shasum -a 256'' |+| Confirm the file is reachable | ''curl -I http://datastore/path/file.rootfs.img'' | 
 +| Get the checksum | ''curl -s http://.../file.rootfs.img \| shasum -a 256'' |
 | Check syntax | ''terraform fmt && terraform validate'' | | Check syntax | ''terraform fmt && terraform validate'' |
 | Preview | ''terraform plan'' | | Preview | ''terraform plan'' |
 | Apply the upgrade | ''terraform apply -target=IMAGE -target=NODE'' | | Apply the upgrade | ''terraform apply -target=IMAGE -target=NODE'' |
-| Tidy up afterwards | ''terraform plan'' then ''terraform apply'' |+| Catch up afterwards | ''terraform plan'', then ''terraform apply'' |
  
-**The five rules:**+Worth keeping in mind:
  
-  - The version name must exist as a complete ''zedcloud_image'' record before a node can use it. +  * ''base_image'' can only name an image that is already in the image repository. Create the 
-  - ''image_sha256'' must be correct and must never be left blank. +    ''zedcloud_image'' record first, then reference it. 
-  - The **node**, not the controller, downloads the file. It must be reachable from the node. +  * ''image_sha256'' needs to be present and correct, or the node rejects the image. 
-  - Use ''-target'' so an upgrade cannot drag unrelated drift along with it. +  * The node downloads the file itself, so the datastore has to be reachable from the node. 
-  - A green Terraform apply is not a successful upgrade. Check the node.+  * ''-target'' keeps an upgrade separate from unrelated drift. 
 +  * Terraform's output covers the controller; the node's status covers the upgrade.
  
eve-kvm/upgrade-eve-terraform.1790624036.txt.gz · Last modified: by mc