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

Subscribers

People subscribed via source and target branches