Merge ~mitchdz/ubuntu/+source/ec2-hibinit-agent:add-IMDSv2-kinetic into ubuntu/+source/ec2-hibinit-agent:ubuntu/kinetic-devel

Proposed by Mitchell Dzurick
Status: Merged
Approved by: Robie Basak
Approved revision: 339b2abca838267fce70588f796208a86e1ac015
Merge reported by: Mitchell Dzurick
Merged at revision: 339b2abca838267fce70588f796208a86e1ac015
Proposed branch: ~mitchdz/ubuntu/+source/ec2-hibinit-agent:add-IMDSv2-kinetic
Merge into: ubuntu/+source/ec2-hibinit-agent:ubuntu/kinetic-devel
Diff against target: 190 lines (+168/-0)
3 files modified
debian/changelog (+7/-0)
debian/patches/lp1941785-Add-support-for-IMDSv2.patch (+160/-0)
debian/patches/series (+1/-0)
Reviewer Review Type Date Requested Status
Athos Ribeiro (community) Approve
Canonical Server Reporter Pending
Review via email: mp+443146@code.launchpad.net
To post a comment you must log in.
339b2ab... by Mitchell Dzurick

changelog

Revision history for this message
Mitchell Dzurick (mitchdz) wrote :

This has been tested to work on Kinetic

us-west-2 was used as the region for aws (this is important because AMI will change)

# To test the failure
# This test case is written for Mantic but will apply to all distributions
1. Create an amazon EC2 instance with the following properties
   - AMI - ami-03e20864bf1b107ee (For Kinetic)
   - t2.micro
   - encrypted 8GB EBS volume with default key
   - Stop - Hibernate behavior: Enable
   - Metadata accessible : Enable
   - Metadata version Info : V2 only (token required)
2. Wait for instance to say "Running"
3. ssh into your instance
4. Create a test program
```
/bin/cat <<EOM >~/allocate_mem.py
#!/usr/bin/python3
import time

# Allocate 200MB chunk of memory
size = 200 * 1024 * 1024 # 200MB
memory_chunk = bytearray(size)

print("Allocated 200MB of memory.")

# Enter indefinite loop
while True:
    time.sleep(1) # Wait for 1 second

# The script will never reach this point
EOM
```
5. Run a background process
```
python3 ~/allocate_mem.py &
```
6. Hibernate the instance
7. Wait for Instance to be in "Stopped" State
8. Start the intsance
9. ssh into instance
10. Check that the process is running
$ ps aux | grep allocate_mem | grep -v grep
$

# To test the fix
1. Create an amazon EC2 instance with the following properties
   - AMI - ami-03e20864bf1b107ee (For Kinetic)
   - t2.micro
   - encrypted 8GB EBS volume with default key
   - Stop - Hibernate behavior: Enable
   - Metadata accessible : Enable
   - Metadata version Info : V2 only (token required)
2. Wait for instance to say "Running"
3. ssh into your instance
4. add PPA for updated package; apt update && apt upgrade
```
sudo add-apt-repository ppa:mitchdz/ec2-hibinit-agent-add-imdsv2-support -y
sudo apt update -y && sudo apt upgrade -y
```
5. Create a test program
```
/bin/cat <<EOM >~/allocate_mem.py
#!/usr/bin/python3
import time

# Allocate 200MB chunk of memory
size = 200 * 1024 * 1024 # 200MB
memory_chunk = bytearray(size)

print("Allocated 200MB of memory.")

# Enter indefinite loop
while True:
    time.sleep(1) # Wait for 1 second

# The script will never reach this point
EOM
```
6. Run a background process
```
python3 ~/allocate_mem.py &
```
7. Hibernate the instance
8. Wait for Instance to be in "Stopped" State
9. Start the intsance
10. ssh into instance
11. Check that the process is running
$ ps aux | grep allocate_mem | grep -v grep
ubuntu 2532 1.1 23.0 221948 213248 pts/0 S 13:43 0:00 python3 /home/ubuntu/allocate_mem.py

Revision history for this message
Athos Ribeiro (athos-ribeiro) wrote :

Thanks for addressing all the requests, Mitchell. I changed one character in your changelog to make it match the other entries in the imperative form.

Uploaded.

Uploading to ubuntu (via ftp to upload.ubuntu.com):
  Uploading ec2-hibinit-agent_1.0.0-0ubuntu12.1.dsc: done.
  Uploading ec2-hibinit-agent_1.0.0-0ubuntu12.1.debian.tar.xz: done.
  Uploading ec2-hibinit-agent_1.0.0-0ubuntu12.1_source.buildinfo: done.
  Uploading ec2-hibinit-agent_1.0.0-0ubuntu12.1_source.changes: done.
Successfully uploaded packages.

Revision history for this message
Athos Ribeiro (athos-ribeiro) :
review: Approve
Revision history for this message
Athos Ribeiro (athos-ribeiro) wrote :

Hi Mitchell, could you set the status of this one to merged?

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/debian/changelog b/debian/changelog
2index 7d37ebd..6bcc9c9 100644
3--- a/debian/changelog
4+++ b/debian/changelog
5@@ -1,3 +1,10 @@
6+ec2-hibinit-agent (1.0.0-0ubuntu12.1) kinetic; urgency=medium
7+
8+ * d/p/lp1941785-Add-support-for-IMDSv2.patch: allows hibernation of
9+ AWS EC2 instances with IMDSv2 (LP: #1941785)
10+
11+ -- Mitchell Dzurick <mitchell.dzurick@canonical.com> Tue, 16 May 2023 15:43:29 -0700
12+
13 ec2-hibinit-agent (1.0.0-0ubuntu12) kinetic; urgency=medium
14
15 * Swapon with maximum priority right before hibernation. This resolves
16diff --git a/debian/patches/lp1941785-Add-support-for-IMDSv2.patch b/debian/patches/lp1941785-Add-support-for-IMDSv2.patch
17new file mode 100644
18index 0000000..7507c77
19--- /dev/null
20+++ b/debian/patches/lp1941785-Add-support-for-IMDSv2.patch
21@@ -0,0 +1,160 @@
22+Description: Add support for IMDSv2
23+Author: Frederick Lefebvre <fredlef@amazon.com>
24+Origin: backport, https://github.com/aws/amazon-ec2-hibinit-agent/commit/9d9bca5c61fa9256289e68c88bd3747af2f62e28
25+Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/ec2-hibinit-agent/+bug/1941785
26+Last-Update: 2023-05-16
27+---
28+--- a/agent/hibinit-agent
29++++ b/agent/hibinit-agent
30+@@ -18,6 +18,7 @@
31+ import sys
32+ import syslog
33+ import math
34++import requests
35+ from subprocess import check_call, check_output, STDOUT
36+ from threading import Thread
37+ from math import ceil
38+@@ -25,11 +26,6 @@
39+
40+
41+ try:
42+- from urllib.request import urlopen, Request
43+-except ImportError:
44+- from urllib2 import urlopen, Request, HTTPError
45+-
46+-try:
47+ from ConfigParser import ConfigParser, NoSectionError, NoOptionError
48+ except:
49+ from configparser import ConfigParser, NoSectionError, NoOptionError
50+@@ -41,7 +37,12 @@
51+ log_to_syslog = True
52+ log_to_stderr = True
53+ SWAP_FILE = '/swap-hibinit'
54+-URL = "http://169.254.169.254/latest/meta-data/hibernation/configured"
55++
56++DEFAULT_STATE_DIR = '/var/lib/hibinit-agent'
57++HIB_ENABLED_FILE = "hibernation-enabled"
58++IMDS_BASEURL = 'http://169.254.169.254'
59++IMDS_API_TOKEN_PATH = 'latest/api/token'
60++IMDS_SPOT_ACTION_PATH = 'latest/meta-data/hibernation/configured'
61+
62+ def log(message):
63+ if log_to_syslog:
64+@@ -314,6 +315,9 @@
65+ get_int('swap', 'percentage-of-ram'), args.swap_ram_percentage, 100)
66+ self.swap_mb = self.merge(
67+ get_int('swap', 'target-size-mb'), args.swap_target_size_mb, 4000)
68++ self.state_dir = get('core', 'state-dir')
69++ if self.state_dir is None:
70++ self.state_dir = DEFAULT_STATE_DIR
71+
72+
73+ def merge(self, cf_value, arg_value, def_val):
74+@@ -337,31 +341,55 @@
75+ def __str__(self):
76+ return str(self.__dict__)
77+
78+-def hibernationEnabled():
79+- """Returns a boolean indicating whether hibernation is enabled or not."""
80+- response = None
81+- try:
82+- response = urlopen(URL)
83+- data = response.read()
84+- if data.lower() in ('false', b'false'):
85+- return False
86+- except:
87+- return False
88+- finally:
89+- if response:
90+- response.close()
91+- return True
92++def get_imds_token(seconds=21600):
93++ """ Get a token to access instance metadata. """
94++ log("Requesting new IMDSv2 token.")
95++ request_header = {'X-aws-ec2-metadata-token-ttl-seconds': str(seconds)}
96++ token_url = '{}/{}'.format(IMDS_BASEURL, IMDS_API_TOKEN_PATH)
97++ response = requests.put(token_url, headers=request_header)
98++ response.close()
99++ if response.status_code != 200:
100++ return None
101++
102++ return response.text
103++
104++def create_state_dir(state_dir):
105++ """ Create agent run dir if it doesn't exists."""
106++ if not os.path.isdir(state_dir):
107++ os.makedirs(state_dir)
108++
109++def hibernation_enabled(state_dir):
110++ """Returns a boolean indicating whether hibernation is enabled or not.
111++ Hibernation can't be enabled/disabled the instance launch. If we find
112++ hibernation to be enabled, we create a semephore file so that we don't
113++ have to probe IMDS again. That is useful when a instance is rebooted
114++ after/if the IMDS http endpoint has been disabled.
115++ """
116++ hib_sem_file = os.path.join(state_dir, HIB_ENABLED_FILE)
117++ if os.path.isfile(hib_sem_file):
118++ log("Found {!r}, configuring hibernation".format(hib_sem_file))
119++ return True
120++
121++ imds_token = get_imds_token()
122++ if imds_token is None:
123++ # IMDS http endpoint is disabled
124++ return False
125++
126++ request_header = {'X-aws-ec2-metadata-token': imds_token}
127++ response = requests.get("{}/{}".format(IMDS_BASEURL, IMDS_SPOT_ACTION_PATH),
128++ headers=request_header)
129++ response.close()
130++ if response.status_code != 200 or response.text.lower() == "false":
131++ return False
132++
133++ log("Hibernation Configured Flag found")
134++ os.mknod(hib_sem_file)
135++
136++ return True
137++
138+
139+ def main():
140+
141+- if not hibernationEnabled():
142+- log("Instance Launch has not enabled Hibernation Configured Flag. hibinit-agent exiting!!")
143+- exit(0)
144+- # Validate if disk space>total RAM
145+- ram_bytes = os.sysconf('SC_PAGE_SIZE') * os.sysconf('SC_PHYS_PAGES')
146+- if get_rootfs_size()<=(math.ceil(float(ram_bytes)/(1024*1024*1024))):
147+- log("Insufficient disk space. Cannot create setup for hibernation. Please allocate a larger root device")
148+- exit(1)
149+ # Parse arguments
150+ parser = argparse.ArgumentParser(description="An EC2 background process that creates a setup for instance hibernation "
151+ "at instance launch and also registers ACPI sleep event/actions")
152+@@ -388,6 +416,17 @@
153+ log_to_syslog = config.log_to_syslog
154+
155+ log("Effective config: %s" % config)
156++ create_state_dir(config.state_dir)
157++
158++ # Let's first check if we even need to run
159++ if not hibernation_enabled(config.state_dir):
160++ log("Instance Launch has not enabled Hibernation Configured Flag. hibinit-agent exiting!!")
161++ exit(0)
162++ # Validate if disk space>total RAM
163++ ram_bytes = os.sysconf('SC_PAGE_SIZE') * os.sysconf('SC_PHYS_PAGES')
164++ if get_rootfs_size()<=(math.ceil(float(ram_bytes)/(1024*1024*1024))):
165++ log("Insufficient disk space. Cannot create setup for hibernation. Please allocate a larger root device")
166++ exit(1)
167+
168+ target_swap_size = config.swap_mb * 1024 * 1024
169+ swap_percentage_size = ram_bytes * config.swap_percentage // 100
170+--- a/etc/hibinit-config.cfg
171++++ b/etc/hibinit-config.cfg
172+@@ -11,6 +11,9 @@
173+ # filesystems.
174+ touch-swap = False
175+
176++# Location where to create any state files
177++state-dir = "/var/lib/hibinit-agent"
178++
179+ [swap]
180+ # If there's no swap then we create it to be equal to the specified
181+ # percentage of RAM or to the target size, whichever is greater
182diff --git a/debian/patches/series b/debian/patches/series
183index 93ae156..7412d06 100644
184--- a/debian/patches/series
185+++ b/debian/patches/series
186@@ -8,3 +8,4 @@ detect-hibernate-cmd-by-default.patch
187 0008-Always-set-resume-device-by-PARTUUID-instead-of-by-d.patch
188 0010-Update-grub-configuration-when-it-needs-an-update.patch
189 lp1968805-Swapon-with-maximum-priority-before-hibernation.patch
190+lp1941785-Add-support-for-IMDSv2.patch

Subscribers

People subscribed via source and target branches