Skip to content

Commit ff212e2

Browse files
nikhiljhaEmersonDoveedithllontop1
authored
k8s -> review (#94)
Co-authored-by: Emerson Dove <[email protected]> Co-authored-by: Edith Llontop <[email protected]> Former-commit-id: ce3a1af
1 parent 458d215 commit ff212e2

File tree

14 files changed

+598
-24
lines changed

14 files changed

+598
-24
lines changed

fogros2/fogros2/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,7 @@
3232
# MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
3333

3434
from .aws_cloud_instance import AWSCloudInstance # noqa: F401
35+
from .gcp_cloud_instance import GCPCloudInstance
36+
from .kubernetes.generic import KubeInstance
3537
from .cloud_node import CloudNode # noqa: F401
3638
from .launch_description import FogROSLaunchDescription # noqa: F401

fogros2/fogros2/aws_cloud_instance.py

100755100644
File mode changed.

fogros2/fogros2/cloud_instance.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ def __init__(
7272
self.cyclone_builder = None
7373
self.scp = None
7474
self._ip = None
75+
self._vpn_ip = None
7576
self.ros_workspace = ros_workspace
7677
self.ros_distro = os.getenv("ROS_DISTRO")
7778
self.logger.debug(f"Using ROS workspace: {self.ros_workspace}")
@@ -84,6 +85,7 @@ def __init__(
8485
self.cloud_service_provider = None
8586
self.dockers = []
8687
self.launch_foxglove = launch_foxglove
88+
self._username = 'ubuntu'
8789

8890
@abc.abstractmethod
8991
def create(self):
@@ -102,15 +104,23 @@ def info(self, flush_to_disk=True):
102104
with open(os.path.join(self._working_dir, "info"), "w+") as f:
103105
json.dump(info_dict, f)
104106
return info_dict
107+
108+
def force_start_vpn(self):
109+
return True
105110

106111
def connect(self):
107-
self.scp = SCPClient(self._ip, self._ssh_key_path)
112+
self.scp = SCPClient(self._ip, self._ssh_key_path, username=self._username)
108113
self.scp.connect()
109114

110115
@property
111116
def ip(self):
112117
return self._ip
113118

119+
@property
120+
def vpn_ip(self):
121+
# Use this when the VPN IP is not None.
122+
return self._vpn_ip
123+
114124
@property
115125
def is_created(self):
116126
return self._is_created
@@ -125,7 +135,7 @@ def apt_install(self, args):
125135
)
126136

127137
def pip_install(self, args):
128-
self.scp.execute_cmd(f"sudo pip3 install {args}")
138+
self.scp.execute_cmd(f"python3 -m pip install {args}")
129139

130140
def install_cloud_dependencies(self):
131141
self.apt_install("wireguard unzip docker.io python3-pip ros-humble-rmw-cyclonedds-cpp")
@@ -177,7 +187,7 @@ def configure_rosbridge(self):
177187
rosbridge_launch_script = (
178188
"ssh -o StrictHostKeyChecking=no -i "
179189
f"{self._ssh_key_path}"
180-
" ubuntu@"
190+
f" {self._username}@"
181191
f"{self._ip}"
182192
f' "source /opt/ros/{self.ros_distro}/setup.bash && '
183193
'ros2 launch rosbridge_server rosbridge_websocket_launch.xml &"'
@@ -229,17 +239,17 @@ def push_and_setup_vpn(self):
229239

230240
def configure_DDS(self):
231241
# configure DDS
232-
self.cyclone_builder = CycloneConfigBuilder(["10.0.0.1"])
242+
self.cyclone_builder = CycloneConfigBuilder(["10.0.0.1"], username=self._username)
233243
self.cyclone_builder.generate_config_file()
234244
self.scp.send_file("/tmp/cyclonedds.xml", "~/cyclonedds.xml")
235245

236246
def launch_cloud_node(self):
237247
cmd_builder = BashBuilder()
238248
cmd_builder.append(f"source /opt/ros/{self.ros_distro}/setup.bash")
239249
cmd_builder.append(
240-
"cd /home/ubuntu/fog_ws && colcon build --cmake-clean-cache"
250+
f"cd /home/{self._username}/fog_ws && colcon build --cmake-clean-cache"
241251
)
242-
cmd_builder.append(". /home/ubuntu/fog_ws/install/setup.bash")
252+
cmd_builder.append(f". /home/{self._username}/fog_ws/install/setup.bash")
243253
cmd_builder.append(self.cyclone_builder.env_cmd)
244254
ros_domain_id = os.environ.get("ROS_DOMAIN_ID")
245255
if not ros_domain_id:

fogros2/fogros2/dds_config_builder.py

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -53,24 +53,18 @@ def generate_config_file(self):
5353

5454

5555
class CycloneConfigBuilder(DDSConfigBuilder):
56-
def __init__(self, ip_addresses):
56+
def __init__(self, ip_addresses, username='ubuntu'):
5757
super().__init__(ip_addresses)
5858
self.config_save_path = "/tmp/cyclonedds.xml"
5959
self.env_cmd = (
6060
"export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp && "
61-
"export CYCLONEDDS_URI=file:///home/ubuntu/cyclonedds.xml"
61+
f"export CYCLONEDDS_URI=file:///home/{username}/cyclonedds.xml"
6262
)
6363

64-
def generate_config_file(self):
65-
if ubuntu_release == "20.04":
66-
interfaces = """
67-
<NetworkInterfaceAddress>wg0</NetworkInterfaceAddress>
68-
<AllowMulticast>false</AllowMulticast>
69-
"""
70-
else:
71-
interfaces = """
64+
def generate_config_file(self, extra_peers = []):
65+
interfaces = """
7266
<Interfaces>
73-
<NetworkInterface name="wg0"/>
67+
<NetworkInterface name="wg0" />
7468
</Interfaces>
7569
"""
7670

@@ -82,6 +76,8 @@ def generate_config_file(self):
8276
'cyclonedds/master/etc/cyclonedds.xsd"'
8377
)
8478

79+
peer_xml = "".join(f"<Peer address=\"{peer}\"/>\n" for peer in extra_peers)
80+
8581
template = f"""
8682
<?xml version="1.0" encoding="UTF-8" ?>
8783
<CycloneDDS {xmlvals}>
@@ -91,6 +87,7 @@ def generate_config_file(self):
9187
<Peers>
9288
<Peer address="10.0.0.1"/>
9389
<Peer address="10.0.0.2"/>
90+
{peer_xml}
9491
</Peers>
9592
<ParticipantIndex>auto</ParticipantIndex>
9693
</Discovery>
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
# Copyright 2022 The Regents of the University of California (Regents)
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
#
15+
# Copyright ©2022. The Regents of the University of California (Regents).
16+
# All Rights Reserved. Permission to use, copy, modify, and distribute this
17+
# software and its documentation for educational, research, and not-for-profit
18+
# purposes, without fee and without a signed licensing agreement, is hereby
19+
# granted, provided that the above copyright notice, this paragraph and the
20+
# following two paragraphs appear in all copies, modifications, and
21+
# distributions. Contact The Office of Technology Licensing, UC Berkeley, 2150
22+
# Shattuck Avenue, Suite 510, Berkeley, CA 94720-1620, (510) 643-7201,
23+
# [email protected], http://ipira.berkeley.edu/industry-info for commercial
24+
# licensing opportunities. IN NO EVEpNT SHALL REGENTS BE LIABLE TO ANY PARTY
25+
# FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
26+
# INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
27+
# DOCUMENTATION, EVEN IF REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
28+
# DAMAGE. REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
29+
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
30+
# PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY,
31+
# PROVIDED HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE
32+
# MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
33+
34+
import json
35+
import os
36+
37+
import subprocess
38+
import uuid
39+
40+
from .cloud_instance import CloudInstance
41+
42+
from .util import extract_bash_column
43+
44+
45+
class GCPCloudInstance(CloudInstance):
46+
"""GCP Implementation of CloudInstance."""
47+
48+
def __init__(
49+
self,
50+
project_id,
51+
ami_image='projects/ubuntu-os-cloud/global/images/ubuntu-2204-jammy-v20220712a',
52+
zone="us-central1-a",
53+
machine_type="e2-medium",
54+
disk_size=10,
55+
**kwargs,
56+
):
57+
super().__init__(**kwargs)
58+
self.cloud_service_provider = "GCP"
59+
60+
id_ = str(uuid.uuid4())[0:8]
61+
self._name = f'fog-{id_}-{self._name}'
62+
63+
self.zone = zone
64+
self.type = machine_type
65+
self.compute_instance_disk_size = disk_size # GB
66+
self.gcp_ami_image = ami_image
67+
68+
self._working_dir = os.path.join(self._working_dir_base, self._name)
69+
os.makedirs(self._working_dir, exist_ok=True)
70+
71+
self._project_id = project_id
72+
73+
# after config
74+
self._ssh_key = None
75+
76+
self.create()
77+
78+
def create(self):
79+
self.logger.info(f"Creating new GCP Compute Engine instance with name {self._name}")
80+
self.create_compute_engine_instance()
81+
self.info(flush_to_disk=True)
82+
self.connect()
83+
self.install_ros()
84+
self.install_colcon()
85+
self.install_cloud_dependencies()
86+
self.push_ros_workspace()
87+
self.info(flush_to_disk=True)
88+
self._is_created = True
89+
90+
def info(self, flush_to_disk=True):
91+
info_dict = super().info(flush_to_disk)
92+
info_dict["compute_region"] = self.zone
93+
info_dict["compute_instance_type"] = self.type
94+
info_dict["disk_size"] = self.compute_instance_disk_size
95+
info_dict["compute_instance_id"] = self._name
96+
if flush_to_disk:
97+
with open(os.path.join(self._working_dir, "info"), "w+") as f:
98+
json.dump(info_dict, f)
99+
return info_dict
100+
101+
def create_compute_engine_instance(self):
102+
os.system(f'gcloud config set project {self._project_id}')
103+
104+
result = subprocess.check_output(f'gcloud compute instances create {self._name} '
105+
f'--project={self._project_id} --zone={self.zone} --machine-type={self.type} '
106+
'--network-interface=network-tier=PREMIUM,subnet=default '
107+
'--maintenance-policy=MIGRATE --provisioning-model=STANDARD '
108+
'--scopes=https://www.googleapis.com/auth/devstorage.read_only,'
109+
'https://www.googleapis.com/auth/logging.write,'
110+
'https://www.googleapis.com/auth/monitoring.write,'
111+
'https://www.googleapis.com/auth/servicecontrol,'
112+
'https://www.googleapis.com/auth/service.management.readonly,'
113+
'https://www.googleapis.com/auth/trace.append '
114+
'--create-disk=auto-delete=yes,'
115+
'boot=yes,'
116+
f'device-name={self._name},'
117+
f'image={self.gcp_ami_image},'
118+
'mode=rw,'
119+
f'size={self.compute_instance_disk_size},'
120+
f'type=projects/{self._project_id}/zones/{self.zone}/diskTypes/pd-balanced '
121+
'--no-shielded-secure-boot '
122+
'--shielded-vtpm '
123+
'--shielded-integrity-monitoring '
124+
'--reservation-affinity=any', shell=True).decode()
125+
126+
# Grab external IP
127+
ip = extract_bash_column(result, 'EXTERNAL_IP')
128+
129+
# Verifies the response was an ip
130+
if len(ip.split('.')) != 4:
131+
raise Exception(f'Error creating instance: {ip}')
132+
133+
self._ip = ip
134+
135+
# Generate SSH keys
136+
os.system(f"printf '\n\n' | gcloud compute ssh {self._name} --zone {self.zone}")
137+
138+
user = subprocess.check_output('whoami', shell=True).decode().strip()
139+
140+
# Username
141+
self._username = (open(f'/home/{user}/.ssh/google_compute_engine.pub').
142+
read()).split(' ')[-1].strip().split('@')[0]
143+
144+
self._ssh_key_path = f'/home/{user}/.ssh/google_compute_engine'
145+
self._is_created = True
146+
147+
self.logger.info(
148+
f"Created {self.type} instance named {self._name} "
149+
f"with id {self._name} and public IP address {self._ip}"
150+
)

fogros2/fogros2/kubernetes/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)