Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update API #942

Merged
merged 7 commits into from
Jun 29, 2020
Merged

Update API #942

merged 7 commits into from
Jun 29, 2020

Conversation

etungsten
Copy link
Contributor

@etungsten etungsten commented Jun 12, 2020

Issue number:
N/A

Adds a set of API endpoints to apiserver that exposes OS update information and manages OS updates.
Adds a rust binary, thar-be-updates that apiserver uses to dispatch update commands.
Adds new updog changes to support the update API

thar-be-updates models the Bottlerocket update process after a state machine:
update-api-states-mark2(6)

Description of changes:

Author: Erikson Tung <[email protected]>
Date:   Mon Jun 8 14:30:21 2020 -0700

    models: new 'FriendlyVersion' model type
    
    Adds a new model type to represent versions that can optionally be
    prefixed with 'v' or represeted by "latest"
Author: Erikson Tung <[email protected]>
Date:   Mon Jun 8 14:33:16 2020 -0700

    settings: add new 'version-lock' and 'ignore-waves' settings
    
    Adds a 'version-lock' setting for specifying the version to update to
    when updating via the API.
    
    Adds a 'ignore-waves' setting for specifying whether to respect update
    waves when updating via the API.
Author: Erikson Tung <[email protected]>
Date:   Thu Jun 11 09:14:08 2020 -0700

    signpost::State: make next(), active(), inactive() public
    
    Exposes 'next()', 'active()', 'inactive()' and consequently 'SetSelect' fo
r
    external crate use.
Author: Erikson Tung <[email protected]>
Date:   Wed Jun 17 11:04:03 2020 -0700

    updog: new subcommand to revert 'update-apply'
    
    Adds a new subcommand for reverting actions done by 'update-apply'.
    Used to "deactivate" an update.
Author: Erikson Tung <[email protected]>
Date:   Wed Jun 17 10:52:25 2020 -0700

    updog: use version-lock and ignore-wave setting for default behavior
    
    updog reads the 'version-lock' and 'ignore-wave' settings for
    determining default update behavior. Still allows overrides via the
    command line options.
Author: Erikson Tung <[email protected]>
Date:   Thu Jun 11 10:20:42 2020 -0700

    thar-be-updates: a Bottlerocket update dispatcher
    
    Adds a new crate 'thar-be-updates' that serves as the interface for
    `apiserver` to dispatch updates.
Author: Erikson Tung <[email protected]>
Date:   Thu Jun 11 13:52:40 2020 -0700

    apiserver: adds update actions API
    
    Extends the Bottlerocket API with endpoints to do updates.

    New action endpoints:

    * /actions/refresh-update
    * /actions/prepare-update
    * /actions/activate-update
    * /actions/deactivate-update
    * /updates/status

    'apiserver' uses 'thar-be-updates' to dispatch update commands.

Testing done:

Testing the updog changes:

Build an image with release version set to 0.3.2, launched instance where version-lock is set to 0.3.3. Then tried the following:

Updog configuration generated properly.

bash-5.0# cat /etc/updog.toml 
metadata_base_url = "https://updates.bottlerocket.aws/2020-02-02/aws-k8s-1.15/x86_64/"
targets_base_url = "https://updates.bottlerocket.aws/targets/"
seed = 191
version_lock = "v0.3.3"
ignore_waves = false
bash-5.0# cat /etc/os-release 
NAME=Bottlerocket
ID=bottlerocket
PRETTY_NAME="Bottlerocket OS 0.3.2"
VARIANT_ID=aws-k8s-1.15
VERSION_ID=0.3.2
BUILD_ID=7192d50a-dirty

Initiate update with updog update-image and see that by default, updog updates to the version-locked version (0.3.3 in this case).

bash-5.0# updog whats      
aws-k8s-1.15 0.3.3
bash-5.0# updog whats --all
aws-k8s-1.15 0.3.4
aws-k8s-1.15 0.3.3
aws-k8s-1.15 0.3.2
aws-k8s-1.15 0.3.1
aws-k8s-1.15 0.3.0
bash-5.0# updog update-image  
Starting update to 0.3.3
Update applied: aws-k8s-1.15 0.3.3

I could also update the boot flags and then revert it via updog revert-update-apply

bash-5.0# updog update-apply
bash-5.0# signpost status
OS disk: /dev/nvme0n1
Set A:   boot=/dev/nvme0n1p2 root=/dev/nvme0n1p3 hash=/dev/nvme0n1p4 priority=1 tries_left=0 successful=true
Set B:   boot=/dev/nvme0n1p6 root=/dev/nvme0n1p7 hash=/dev/nvme0n1p8 priority=2 tries_left=1 successful=false
Active:  Set A
Next:    Set B

bash-5.0# updog revert-update-apply   
bash-5.0# signpost status          
OS disk: /dev/nvme0n1
Set A:   boot=/dev/nvme0n1p2 root=/dev/nvme0n1p3 hash=/dev/nvme0n1p4 priority=2 tries_left=0 successful=true
Set B:   boot=/dev/nvme0n1p6 root=/dev/nvme0n1p7 hash=/dev/nvme0n1p8 priority=0 tries_left=1 successful=false
Active:  Set A
Next:    Set A

bash-5.0# updog update-apply       
bash-5.0# signpost status   
OS disk: /dev/nvme0n1
Set A:   boot=/dev/nvme0n1p2 root=/dev/nvme0n1p3 hash=/dev/nvme0n1p4 priority=1 tries_left=0 successful=true
Set B:   boot=/dev/nvme0n1p6 root=/dev/nvme0n1p7 hash=/dev/nvme0n1p8 priority=2 tries_left=1 successful=false
Active:  Set A
Next:    Set B
bash-5.0# 

Testing the update API

Built custom image with a 0.3.2 version tag. Launched instance, tried the following (version-lock set to 'latest'):
(I pretty-printed the json output for clarity)

[ec2-user@ip-192-168-0-240 ~]$ apiclient -u /updates/status
Status 404 when GETing unix://2f72756e2f6170692e736f636b:0/updates/status: Update status is uninitialized, refresh-updates to initialize it
[ec2-user@ip-192-168-0-240 ~]$ apiclient -u /actions/refresh-updates -m POST
[ec2-user@ip-192-168-0-240 ~]$ apiclient -u /updates/status
{
  "update_state": "Available",
  "available_updates": [
    "0.4.0",
    "0.3.4",
    "0.3.3",
    "0.3.2",
    "0.3.1",
    "0.3.0"
  ],
  "chosen_update": {
    "arch": "x86_64",
    "version": "0.4.0",
    "variant": "aws-k8s-1.15"
  },
  "active_partition": {
    "image": {
      "arch": "x86_64",
      "version": "0.3.4",
      "variant": "aws-k8s-1.15"
    },
    "next_to_boot": true
  },
  "staging_partition": null,
  "most_recent_command": {
    "cmd_type": "refresh",
    "cmd_status": "Success",
    "timestamp": "2020-07-10T06:42:52.284285751Z",
    "exit_status": 0,
    "stderr": ""
  }
}
[ec2-user@ip-192-168-0-240 ~]$ apiclient -v -u /actions/prepare-update -m POST
204 No Content

Here you can see apiserver trying to obtain the shareable lock to read the update status file. Caller gets 423 responses when the lock is held by t-b-u.

[ec2-user@ip-192-168-0-240 ~]$ apiclient -u /updates/status
Status 423 when GETing unix://2f72756e2f6170692e736f636b:0/updates/status: Unable to obtain shared lock for reading update status: Resource temporarily unavailable (os error 11)
[ec2-user@ip-192-168-0-240 ~]$ apiclient -u /updates/status
Status 423 when GETing unix://2f72756e2f6170692e736f636b:0/updates/status: Unable to obtain shared lock for reading update status: Resource temporarily unavailable (os error 11)
[ec2-user@ip-192-168-0-240 ~]$ apiclient -u /updates/status
Status 423 when GETing unix://2f72756e2f6170692e736f636b:0/updates/status: Unable to obtain shared lock for reading update status: Resource temporarily unavailable (os error 11)
[ec2-user@ip-192-168-0-240 ~]$ apiclient -u /updates/status
Status 423 when GETing unix://2f72756e2f6170692e736f636b:0/updates/status: Unable to obtain shared lock for reading update status: Resource temporarily unavailable (os error 11)
[ec2-user@ip-192-168-0-240 ~]$ apiclient -u /updates/status
{
  "update_state": "Staged",
  "available_updates": [
    "0.4.0",
    "0.3.4",
    "0.3.3",
    "0.3.2",
    "0.3.1",
    "0.3.0"
  ],
  "chosen_update": {
    "arch": "x86_64",
    "version": "0.4.0",
    "variant": "aws-k8s-1.15"
  },
  "active_partition": {
    "image": {
      "arch": "x86_64",
      "version": "0.3.4",
      "variant": "aws-k8s-1.15"
    },
    "next_to_boot": true
  },
  "staging_partition": {
    "image": {
      "arch": "x86_64",
      "version": "0.4.0",
      "variant": "aws-k8s-1.15"
    },
    "next_to_boot": false
  },
  "most_recent_command": {
    "cmd_type": "prepare",
    "cmd_status": "Success",
    "timestamp": "2020-07-10T06:44:27.982261529Z",
    "exit_status": 0,
    "stderr": "Starting update to 0.4.0\n"
  }
}
[ec2-user@ip-192-168-0-240 ~]$ apiclient -u /actions/activate-update -m POST
[ec2-user@ip-192-168-0-240 ~]$ apiclient -u /updates/status
{
  "update_state": "Ready",
  "available_updates": [
    "0.4.0",
    "0.3.4",
    "0.3.3",
    "0.3.2",
    "0.3.1",
    "0.3.0"
  ],
  "chosen_update": {
    "arch": "x86_64",
    "version": "0.4.0",
    "variant": "aws-k8s-1.15"
  },
  "active_partition": {
    "image": {
      "arch": "x86_64",
      "version": "0.3.4",
      "variant": "aws-k8s-1.15"
    },
    "next_to_boot": false
  },
  "staging_partition": {
    "image": {
      "arch": "x86_64",
      "version": "0.4.0",
      "variant": "aws-k8s-1.15"
    },
    "next_to_boot": true
  },
  "most_recent_command": {
    "cmd_type": "activate",
    "cmd_status": "Success",
    "timestamp": "2020-07-10T06:47:19.903337270Z",
    "exit_status": 0,
    "stderr": ""
  }
}
[ec2-user@ip-192-168-0-240 ~]$ apiclient -u /actions/deactivate-update -m POST
[ec2-user@ip-192-168-0-240 ~]$ apiclient -u /updates/status
{"update_state":"Staged","available_updates":["0.4.0","0.3.4","0.3.3","0.3.2","0.3.1","0.3.0"],"chosen_update":{"arch":"x86_64","version":"0.4.0","variant":"aws-k8s-1.15"},"active_partition":{"image":{"arch":"x86_64","version":"0.3.4","variant":"aws-k8s-1.15"},"next_to_boot":true},"staging_partition":{"image":{"arch":"x86_64","version":"0.4.0","variant":"aws-k8s-1.15"},"next_to_boot":false},"most_recent_command":{"cmd_type":"deactivate","cmd_status":"Success","timestamp":"2020-07-10T15:55:09.238751262Z","exit_status":0,"stderr":""}}
[ec2-user@ip-192-168-0-240 ~]$ apiclient -u /actions/activate-update -m POST
[ec2-user@ip-192-168-0-240 ~]$ apiclient -u /updates/status
{"update_state":"Ready","available_updates":["0.4.0","0.3.4","0.3.3","0.3.2","0.3.1","0.3.0"],"chosen_update":{"arch":"x86_64","version":"0.4.0","variant":"aws-k8s-1.15"},"active_partition":{"image":{"arch":"x86_64","version":"0.3.4","variant":"aws-k8s-1.15"},"next_to_boot":false},"staging_partition":{"image":{"arch":"x86_64","version":"0.4.0","variant":"aws-k8s-1.15"},"next_to_boot":true},"most_recent_command":{"cmd_type":"activate","cmd_status":"Success","timestamp":"2020-07-10T15:55:51.310418363Z","exit_status":0,"stderr":""}}
[ec2-user@ip-192-168-0-240 ~]$ apiclient -u /actions/reboot -m POST

Please let me know if there are any other scenarios you'd like me to run through.

Terms of contribution:

By submitting this pull request, I agree that this contribution is dual-licensed under the terms of both the Apache License, version 2.0, and the MIT license.

@etungsten
Copy link
Contributor Author

Push above fixes some minor comment typos and documents the new API settings version-lock and ignore-waves.

Copy link
Contributor

@tjkirch tjkirch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before reading the details, my first reaction to the testing is that the list of available_updates is already too big for the API output to be useful on the command line, and almost too big for it to be readable even after pretty-printing, and that's with only a few updates in the repo. It'll grow for a long time, and I think make the API output essentially unusable.

I think the easiest thing to do is splitting available_updates into its own GET, /updates/available. At least the main status will be usable then. I still worry about the available data being unwieldy, and I wonder if we should further split that into one call that returns a simple list (["0.3.2", "0.3.3", "0.3.4"]) and another call for details on one/all versions.

We could also discuss whether the simple version list could be included in the main status API, and /updates/available is the detail. (I'd still have some level of worry about any unbounded data source in the main status.) Perhaps the detail could have a filter to get details for a single version instead of all?

Boiling that down, I think this would be my proposal:

  • /updates/status - change available_updates to a simple version list.
  • /updates/available - returns the detail that's in available_updates now.
  • /updates/v0.3.4 - a way to represent the available_updates information about a single update

Copy link
Contributor

@webern webern left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Saving some thoughts/ideas, but main.rs 'diff' was hidden so I was confused. Will come back again and review the main program! Nice work.

sources/models/src/modeled_types/shared.rs Show resolved Hide resolved
sources/api/thar-be-updates/src/status.rs Outdated Show resolved Hide resolved
sources/api/thar-be-updates/src/status.rs Outdated Show resolved Hide resolved
sources/api/thar-be-updates/src/status.rs Show resolved Hide resolved
sources/api/thar-be-updates/src/status.rs Outdated Show resolved Hide resolved
@etungsten
Copy link
Contributor Author

etungsten commented Jun 15, 2020

Push above addresses @tjkirch 's comment.

Added new API paths:

  • /updates/available, lists all the available update versions and their information.
  • /updates/version-info/{version}, lists information for a specified update version.

Added functionality to thar-be-updates to support the above.

Testing done:
Built image with version 0.3.2 in Release.toml and tried the following:

[ec2-user@ip-192-168-8-198 ~]$ apiclient -u /updates/status
{
  "update_state": "Idle",
  "available_updates": [],
  "chosen_update": null,
  "active_partition": {
    "image": {
      "arch": "x86_64",
      "version": "0.3.2",
      "variant": "aws-k8s-1.15"
    },
    "next_to_boot": true
  },
  "staging_partition": null,
  "most_recent_command": null
}
[ec2-user@ip-192-168-8-198 ~]$ apiclient -u /updates/available
[]
[ec2-user@ip-192-168-8-198 ~]$ apiclient -u /updates/v0.3.1
Status 404 when GETing unix://2f72756e2f6170692e736f636b:0/updates/v0.3.1: Chosen update missing: Queried update version 'v0.3.1' does not exist

[ec2-user@ip-192-168-8-198 ~]$ apiclient -u /actions/refresh-update -m POST
[ec2-user@ip-192-168-8-198 ~]$ apiclient -u /updates/status
{
  "update_state": "Available",
  "available_updates": [
    "0.3.4",
    "0.3.3",
    "0.3.2",
    "0.3.1",
    "0.3.0"
  ],
  "chosen_update": {
    "arch": "x86_64",
    "version": "0.3.4",
    "variant": "aws-k8s-1.15"
  },
  "active_partition": {
    "image": {
      "arch": "x86_64",
      "version": "0.3.2",
      "variant": "aws-k8s-1.15"
    },
    "next_to_boot": true
  },
  "staging_partition": null,
  "most_recent_command": {
    "cmd_type": "refresh-update",
    "cmd_status": "Success",
    "timestamp": "2020-06-15T23:18:14.536344234Z",
    "exit_status": 0,
    "stderr": ""
  }
}
[ec2-user@ip-192-168-8-198 ~]$ apiclient -u /updates/available
[
  {
    "variant": "aws-k8s-1.15",
    "arch": "x86_64",
    "version": "0.3.4",
    "max_version": "0.3.4",
    "waves": {
      "0": "2020-05-28T18:00:00Z",
      "20": "2020-05-28T21:00:00Z",
      "102": "2020-05-29T17:00:00Z",
      "204": "2020-05-31T17:00:00Z",
      "512": "2020-06-03T17:00:00Z"
    },
    "images": {
      "boot": "bottlerocket-aws-k8s-1.15-x86_64-0.3.4-85d09a8-boot.ext4.lz4",
      "root": "bottlerocket-aws-k8s-1.15-x86_64-0.3.4-85d09a8-root.ext4.lz4",
      "hash": "bottlerocket-aws-k8s-1.15-x86_64-0.3.4-85d09a8-root.verity.lz4"
    }
  },
  {
    "variant": "aws-k8s-1.15",
    "arch": "x86_64",
    "version": "0.3.3",
    "max_version": "0.3.4",
    "waves": {
      "0": "2020-05-14T21:02:06.101007479Z",
      "61": "2020-05-15T00:02:06.101016645Z",
      "245": "2020-05-15T04:02:06.101021535Z",
      "1024": "2020-05-15T20:02:06.101025162Z"
    },
    "images": {
      "boot": "bottlerocket-aws-k8s-1.15-x86_64-0.3.3-b7d91846-boot.ext4.lz4",
      "root": "bottlerocket-aws-k8s-1.15-x86_64-0.3.3-b7d91846-root.ext4.lz4",
      "hash": "bottlerocket-aws-k8s-1.15-x86_64-0.3.3-b7d91846-root.verity.lz4"
    }
  },
  {
    "variant": "aws-k8s-1.15",
    "arch": "x86_64",
    "version": "0.3.2",
    "max_version": "0.3.4",
    "waves": {
      "0": "2020-04-20T23:15:20.890251663Z",
      "20": "2020-04-21T02:15:20.890267117Z",
      "102": "2020-04-21T22:15:20.890276237Z",
      "204": "2020-04-23T22:15:20.890287476Z",
      "512": "2020-04-26T22:15:20.890299173Z"
    },
    "images": {
      "boot": "bottlerocket-aws-k8s-1.15-x86_64-0.3.2-25aa08c-boot.ext4.lz4",
      "root": "bottlerocket-aws-k8s-1.15-x86_64-0.3.2-25aa08c-root.ext4.lz4",
      "hash": "bottlerocket-aws-k8s-1.15-x86_64-0.3.2-25aa08c-root.verity.lz4"
    }
  },
  {
    "variant": "aws-k8s-1.15",
    "arch": "x86_64",
    "version": "0.3.1",
    "max_version": "0.3.4",
    "waves": {
      "0": "2020-03-10T19:00:00Z",
      "2048": "2020-03-12T19:00:00Z"
    },
    "images": {
      "boot": "bottlerocket-aws-k8s-1.15-x86_64-0.3.1-8a0c0b3-boot.ext4.lz4",
      "root": "bottlerocket-aws-k8s-1.15-x86_64-0.3.1-8a0c0b3-root.ext4.lz4",
      "hash": "bottlerocket-aws-k8s-1.15-x86_64-0.3.1-8a0c0b3-root.verity.lz4"
    }
  },
  {
    "variant": "aws-k8s-1.15",
    "arch": "x86_64",
    "version": "0.3.0",
    "max_version": "0.3.4",
    "waves": {
      "0": "2020-02-28T16:00:00Z"
    },
    "images": {
      "boot": "bottlerocket-aws-k8s-1.15-x86_64-0.3.0-faaec6e4-boot.ext4.lz4",
      "root": "bottlerocket-aws-k8s-1.15-x86_64-0.3.0-faaec6e4-root.ext4.lz4",
      "hash": "bottlerocket-aws-k8s-1.15-x86_64-0.3.0-faaec6e4-root.verity.lz4"
    }
  }
]
[ec2-user@ip-192-168-8-198 ~]$ apiclient -u /updates/v0.3.1
{
  "variant": "aws-k8s-1.15",
  "arch": "x86_64",
  "version": "0.3.1",
  "max_version": "0.3.4",
  "waves": {
    "0": "2020-03-10T19:00:00Z",
    "2048": "2020-03-12T19:00:00Z"
  },
  "images": {
    "boot": "bottlerocket-aws-k8s-1.15-x86_64-0.3.1-8a0c0b3-boot.ext4.lz4",
    "root": "bottlerocket-aws-k8s-1.15-x86_64-0.3.1-8a0c0b3-root.ext4.lz4",
    "hash": "bottlerocket-aws-k8s-1.15-x86_64-0.3.1-8a0c0b3-root.verity.lz4"
  }
}
[ec2-user@ip-192-168-8-198 ~]$ apiclient -u /updates/latest
{
  "variant": "aws-k8s-1.15",
  "arch": "x86_64",
  "version": "0.3.4",
  "max_version": "0.3.4",
  "waves": {
    "0": "2020-05-28T18:00:00Z",
    "20": "2020-05-28T21:00:00Z",
    "102": "2020-05-29T17:00:00Z",
    "204": "2020-05-31T17:00:00Z",
    "512": "2020-06-03T17:00:00Z"
  },
  "images": {
    "boot": "bottlerocket-aws-k8s-1.15-x86_64-0.3.4-85d09a8-boot.ext4.lz4",
    "root": "bottlerocket-aws-k8s-1.15-x86_64-0.3.4-85d09a8-root.ext4.lz4",
    "hash": "bottlerocket-aws-k8s-1.15-x86_64-0.3.4-85d09a8-root.verity.lz4"
  }
}

sources/models/src/modeled_types/shared.rs Show resolved Hide resolved
sources/api/apiserver/src/server/mod.rs Outdated Show resolved Hide resolved
sources/api/apiserver/src/server/mod.rs Outdated Show resolved Hide resolved
sources/api/apiserver/src/server/mod.rs Outdated Show resolved Hide resolved
sources/api/apiserver/src/server/mod.rs Outdated Show resolved Hide resolved
sources/api/openapi.yaml Outdated Show resolved Hide resolved
@tjkirch
Copy link
Contributor

tjkirch commented Jun 16, 2020

It won't let me respond to @bcressey's comments inline, so here are a couple responses.

Maybe BottlerocketVersion? It's fairly specific to our use case.

Well, the apiserver and models are entirely specific to our use case, so I'm not sure a Bottlerocket prefix makes sense anywhere. The point of this struct is that it gives a friendlier interface to common types of version requests, so I'd vote for the existing name. Or something more like VersionRequest.

I'm not sure we want to read stdin or stderr synchronously in any of these commands. Might be worth looking at tokio::process.

I agree that reading synchronously is a potential problem, but let's please avoid bringing in tokio if we can help it. I'd rather move toward the fire-and-forget path you mentioned in another comment. t-b-u should be able to put any relevant output in the status file, so we shouldn't need stdout/stderr from it, and perhaps we can call it the same way as thar-be-settings then?

@bcressey
Copy link
Contributor

Well, the apiserver and models are entirely specific to our use case, so I'm not sure a Bottlerocket prefix makes sense anywhere. The point of this struct is that it gives a friendlier interface to common types of version requests, so I'd vote for the existing name. Or something more like VersionRequest.

We have BottlerocketRelease today which is where I got the idea. 😄

FriendlyVersion is fine though.

sources/api/thar-be-updates/src/error.rs Outdated Show resolved Hide resolved
sources/api/thar-be-updates/src/main.rs Outdated Show resolved Hide resolved
sources/api/thar-be-updates/src/main.rs Outdated Show resolved Hide resolved
sources/api/thar-be-updates/src/status.rs Outdated Show resolved Hide resolved
sources/api/thar-be-updates/src/main.rs Outdated Show resolved Hide resolved
sources/api/thar-be-updates/src/main.rs Outdated Show resolved Hide resolved
@etungsten etungsten force-pushed the update-api branch 2 times, most recently from 85bdcba to 029ab58 Compare June 16, 2020 20:14
@etungsten
Copy link
Contributor Author

Pushes above addresses @webern 's comments and the majority of @bcressey 's and @tjkirch 's comments.

  • Removed /updates/available and /updates/version-info/{version} API paths and associated supporting code from thar-be-updates.
  • Removed commands that print the update status from thar-be-updates now exposing the update status file for the apiserver to directly read from.
  • Some minor refactoring

I still need to add updog revert-update-apply to updog.

@etungsten
Copy link
Contributor Author

etungsten commented Jun 16, 2020

Push above fixes some issues with directory creation.

  • Now relying on tmpfiles.d to create the directories thar-be-updates needs.
  • The apiserver directly reads the status file for the update status, if thar-be-updates never ran before, there would be no status file created. Added an 404 response to prompt the user to call /actions/refresh-update when that happens.

Also updated testing description in PR description.

Comment on lines 368 to 377
match thar_be_updates::status::get_update_status() {
Ok(update_status) => {
let update_status = update_status.simplify_update_info();
Ok(UpdateStatusResponse(update_status))
}
Err(e) => match e {
thar_be_updates::error::Error::OpenStatusFile { .. } => {
return error::UninitializedUpdateStatus {}.fail()
}
_ => return error::UpdateError {}.fail(),
},
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like its misusing the accessible functionality in tbu. I'm somewhat wary of embedding knowledge and error handling of the tool's internals here. That said, I'm sympathetic to its use and help here.

The only suggestion I can make is to specifically present an "API" to ourselves that namespaces off "safe" functions, types, and helpers to be used in these sort of contexts (eg: thar_be_updates::api or t_b_u::on_disk).

I'll defer to hear from some of the other rustic folks on this.


#[allow(clippy::wrong_self_convention)]
/// Transition update state to Idle if the 'chosen' update version is not available
pub fn to_idle(&mut self) -> Result<()> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking that we might want to parcel out the legal transitions and operations themselves to handle them in a more focused fashion.

There have been a handful of discussions on blogs and such that use the type system of Rust to generate a "state machine" of types (even a macro-ize crate came of it!). It might be worth using richer types to enforce the allowable transitions internally.

Regardless, I would very much like to see the allowable state transition handling in its own type or Trait impl.

Copy link
Contributor Author

@etungsten etungsten Jun 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I explored the trait option early on and realized (or at least it seemed to me from the reading I did) that serde does not play nice when de-serializing into generic types , since I can't tell the compiler the concrete type (state) the status should be de serialized as. For the sake of simplicity I opted for runtime checks here.

Copy link
Contributor Author

@etungsten etungsten Jun 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another thing is that since these states don't internally store any information nor do they do anything upon transitions, the use of types and traits to represent these states are somewhat unnecessary (aside from the compile time checks for transition validity, that's always nice).

Copy link
Contributor Author

@etungsten etungsten Jun 17, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since I can't tell the compiler the concrete type (state) the status should be de serialized as

One way to do this actually is to have a separate enum in the status struct that indicates what type (state) the rest of the struct should be deserialized as. But then at the point I would have to have a enum to represent the state already and I don't really get much from the benefits of having the state as separate types.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rust-lang/rfcs#2593
^ This would be really nice to have for this purpose

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One way to do this actually is to have a separate enum in the status struct that indicates what type (state) the rest of the struct should be deserialized as. But then at the point I would have to have a enum to represent the state already and I don't really get much from the benefits of having the state as separate types.

Yeah, you could lift in and out of a single enum with all states and use a get_state_object(&self) -> impl StateMachinery to read out the "smart transition". But as you said, you'd have to duplicate and that's no bueno. The types themselves would still hold the constraints so that'll still an improvement..

Serde does let you do tagged enums, but you're right about serde not lending a hand w.r.t. nicely deserializing into Trait objects (I wanted the same thing a while back). Which is too bad.

I wish this was nicer to have, we'll probably just have to punt on this until we get something more unwieldy. I still strongly recommend pulling the transition logic into its own type separate from the rest of the driving code. It could be embedded into the serialized data (and even flattened for other consumers if needed).

Copy link
Contributor

@tjkirch tjkirch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First pass on the outlying parts.

sources/models/src/modeled_types/shared.rs Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
packages/os/thar-be-updates-tmpfiles.conf Outdated Show resolved Hide resolved
sources/api/apiserver/Cargo.toml Outdated Show resolved Hide resolved
sources/api/apiserver/src/server/mod.rs Outdated Show resolved Hide resolved
sources/api/apiserver/src/server/mod.rs Outdated Show resolved Hide resolved
sources/models/src/modeled_types/shared.rs Outdated Show resolved Hide resolved
sources/models/src/modeled_types/shared.rs Outdated Show resolved Hide resolved
sources/api/thar-be-updates/README.tpl Outdated Show resolved Hide resolved
@etungsten

This comment has been minimized.

Copy link
Contributor

@tjkirch tjkirch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some thoughts on the OpenAPI spec.

sources/api/openapi.yaml Outdated Show resolved Hide resolved
sources/api/openapi.yaml Outdated Show resolved Hide resolved
sources/api/openapi.yaml Outdated Show resolved Hide resolved
sources/api/openapi.yaml Outdated Show resolved Hide resolved
sources/api/apiserver/src/server/mod.rs Outdated Show resolved Hide resolved
sources/api/openapi.yaml Outdated Show resolved Hide resolved
sources/api/openapi.yaml Outdated Show resolved Hide resolved
@etungsten
Copy link
Contributor Author

Push above addresses @tjkirch 's comments from #942 (review) and #942 (review)

Copy link
Contributor

@tjkirch tjkirch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments on apiserver

sources/updater/updog/src/main.rs Outdated Show resolved Hide resolved
sources/updater/updog/src/main.rs Outdated Show resolved Hide resolved
sources/updater/updog/src/main.rs Outdated Show resolved Hide resolved
sources/api/thar-be-updates/Cargo.toml Show resolved Hide resolved
sources/models/Cargo.toml Outdated Show resolved Hide resolved
sources/api/apiserver/src/server/controller.rs Outdated Show resolved Hide resolved
sources/api/apiserver/src/server/controller.rs Outdated Show resolved Hide resolved
sources/api/apiserver/src/server/mod.rs Outdated Show resolved Hide resolved
sources/api/apiserver/src/server/mod.rs Outdated Show resolved Hide resolved
sources/api/apiserver/src/server/mod.rs Outdated Show resolved Hide resolved
@etungsten
Copy link
Contributor Author

etungsten commented Jun 22, 2020

Rebased onto develop to pull in commit 3f3a26d that addresses #942 (comment)

Addresses @bcressey 's comments.
There are also some minor formatting changes

@etungsten etungsten force-pushed the update-api branch 2 times, most recently from 5ef0998 to 9653b5a Compare June 22, 2020 19:22
@etungsten
Copy link
Contributor Author

etungsten commented Jun 22, 2020

The pushes above addresses @tjkirch 's comments.

Tested changes and things still work same as before as described in the PR description.

@etungsten
Copy link
Contributor Author

Push above fixes the remnants of @tjkirch 's comments.

@iliana iliana added this to the v0.5.0 milestone Jun 23, 2020
Adds a new model type to represent versions that can optionally be
prefixed with 'v' or represeted by "latest"
Adds a 'version-lock' setting for specifying the version to update to
when updating.

Adds a 'ignore-waves' setting for specifying whether to respect update
waves when updating.
updog reads the 'version-lock' and 'ignore-wave' settings for
determining default update behavior. Still allows overrides via the
command line options.
Adds a new subcommand for reverting actions done by 'update-apply'.
Used to "deactivate" an update.
Exposes 'next()', 'active()', 'inactive()' and consequently 'SetSelect' for
external crate use.
Adds a new crate 'thar-be-updates' that serves as the interface for
`apiserver` to dispatch updates.
@etungsten
Copy link
Contributor Author

etungsten commented Jun 24, 2020

Rebase onto develop and resolved some conflicts in updog

sources/api/apiserver/src/server/controller.rs Outdated Show resolved Hide resolved
sources/updater/signpost/src/set.rs Show resolved Hide resolved
Copy link
Contributor

@zmrow zmrow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀

Extends the Bottlerocket API with endpoints to do updates.

New action endpoints:

* /actions/refresh-update
* /actions/prepare-update
* /actions/activate-update
* /actions/deactivate-update
* /updates/status

'apiserver' uses 'thar-be-updates' to dispatch update commands.
@etungsten
Copy link
Contributor Author

Push above addresses #942 (comment).

@etungsten etungsten merged commit 110becb into bottlerocket-os:develop Jun 29, 2020
@etungsten etungsten deleted the update-api branch June 29, 2020 18:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants