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

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

changelog

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

This has been tested to work on Jammy

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

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

Subscribers

People subscribed via source and target branches