blob: b5c5bf7c264aa126daed98e9053c6f940e7abc80 [file] [log] [blame]
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="/packages/assets/bootstrap/bootstrap.min.css">
<title>Pre-Requisites</title>
<link rel="stylesheet" href="/packages/assets/css/style.css">
<script src="/packages/assets/js/code.js"></script>
<meta name="twitter:card" content="summary"/>
<meta name="twitter:image" content="https://eclipse.org/packages/images/icon/twitter_icon.png"/>
<meta name="twitter:title" content="Pre-Requisites"/>
<meta name="twitter:description" content=""/>
<link rel="icon" type="image/svg+xml" href="/packages/images/icon/icon.svg" sizes="any">
<link rel="icon" href="/packages/images/icon/icon.png" sizes="192x192">
<link rel="apple-touch-icon-precomposed" href="/packages/images/icon/icon_apple.png" sizes="180x180">
</head>
<body>
<header>
<nav class="navbar navbar-expand-md fixed-top navbar-light bg-light">
<div class="container">
<a class="navbar-brand" href="/packages/">
<img src="/packages/images/icon_brand_navbar.svg" width="30" height="30" alt="Eclipse IoT Packages™">
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarCollapse">
<ul class="navbar-nav mr-auto"><li class="nav-item">
<a
class="nav-link"
href="/packages/">Home</a>
</li>
<li class="nav-item">
<a
class="nav-link"
href="/packages/about/">About</a>
</li>
<li class="nav-item">
<a
class="nav-link"
href="/packages/faq/">FAQ</a>
</li>
<li class="nav-item">
<a
class="nav-link"
href="/packages/contribute/">Contribute</a>
</li>
<li class="nav-item">
<a
class="nav-link active"
href="/packages/prereqs/">Prerequisites</a>
</li>
<li class="nav-item">
<a
class="nav-link"
href="/packages/repository/">Repository</a>
</li>
</ul>
</div>
</div>
</nav></header>
<main id="main" role="main" >
<div class="container mt-5">
<h1>Pre-Requisites</h1>
<p class="lead">Getting you started with a common set of tools, used by all packages.</p>
</div>
<div class="container">
<div class="row">
<div class="col-12 col-md-8 col-lg-9">
<h2 id="kubernetes-client">Kubernetes Client</h2>
<p>First of all, you will need a command tool named <code class="language-plaintext highlighter-rouge">kubectl</code>. This application allows you to interact with
your Kubernetes cluster from the command line. While Kubernetes also comes with a Web UI, it is much simpler
to document the installation procedures using command line tool. Also does the Web UI change over time, and
with different Kubernetes variants. However the <code class="language-plaintext highlighter-rouge">kubectl</code> tool works with all variations of Kubernetes, as it
uses the standardized API in the background.</p>
<p>You can find more information in the Kubernetes documentation: <a href="https://kubernetes.io/docs/tasks/tools/install-kubectl/">Install and Set Up kubectl</a>.</p>
<h2 id="kubernetes-cluster">Kubernetes Cluster</h2>
<p>For the cloud side environment, you will need an installation of Kubernetes. Kubernetes comes in
different forms, and we try to document a few of them for you. There are other variants as well,
and you are welcome to try all of them. All packages should be able to run on any Kubernetes
you provide.</p>
<p>Generally, you can either use a cloud provider, such as Azure (AKS) or AWS (EKS) to provide a Kubernetes cluster for you, or you can set up one by yourself.
<a href="https://landscape.cncf.io/category=platform&amp;format=card-mode&amp;grouping=category">Here</a> you can find a list of Kubernetes options.
As the Kubernetes API is standardized both ways will work.
However, setting up Kubernetes yourself is not trivial and requires some more effort.
As part of this tutorial, we present two different K8s distributions (and how to deploy Eclipse IoT Packages with them), namely <a href="microk8s">MicroK8s</a> and <a href="minikube">Minikube</a>.</p>
<p>Packages are encouraged to give you an estimate of what resources they require. The following is
an example of what this may look like. You will need to translate this into the specific
Kubernetes environment you have. Also may the package declare on which Kubernetes platform
it was tested. This doesn’t mean that other Kubernetes versions don’t work, but sets some
expectations of what was tested at some point.</p>
<div class="row">
<div class="col-12 col-sm-6 col-md-5 col-lg-4 mx-md-auto mb-3">
<div class="card req-card">
<div class="card-header text-center"><h5>Requirements</h5></div>
<ul class="list-group list-group-flush">
<li class="list-group-item">
<dl class="row">
<dt class="col-sm-6">Kubernetes</dt>
<dd class="col-sm-6 text-right">1.17.x</dd>
</dl>
</li>
<li class="list-group-item">
<dl class="row">
<dt class="col-sm-6">CPUs</dt>
<dd class="col-sm-6 text-right">2</dd>
</dl>
</li>
<li class="list-group-item">
<dl class="row">
<dt class="col-sm-6">Memory</dt>
<dd class="col-sm-6 text-right">1024 MiB</dd>
</dl>
</li>
<li class="list-group-item">
<dl class="row">
<dt class="col-sm-6">Disk</dt>
<dd class="col-sm-6 text-right">40 GiB</dd>
</dl>
</li>
</ul>
<div class="card-body">Additional requirements.</div>
</div>
</div>
</div>
<p>For each documentation Kubernetes environment on this page, you will
find a section that explains how to do this.</p>
<h3 id="microk8s">MicroK8s</h3>
<p><a href="https://microk8s.io/">MicroK8s</a> is a Kubernetes distribution, maintained by Canonical and enables developers to run a fully-fledged Kubernetes cluster on their own infrastructure.
In contrast to minikube it is not only intended for testing purposes but also for production scenarios, e.g. in cases where a cloud setup is not possible or desirable.</p>
<p>All you need is a virtual or physical machine with root access and the <a href="https://snapcraft.io/docs/installing-snapd">snap package manager</a>.</p>
<p>Install MicroK8s, executing <code class="language-plaintext highlighter-rouge">sudo snap install microk8s --classic</code></p>
<p>Check the cluster status, by running <code class="language-plaintext highlighter-rouge">microk8s status --wait-ready</code></p>
<p>Once the cluster is up and running you will need to enable a few modules that are required for installing an Eclipse IoT Packages package, such as Cloud2Edge.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>LOCAL_ADDRESS=$(ip addr show eth0 | grep "inet\b" | awk '{print $2}' | cut -d/ -f1)
microk8s enable metrics-server
microk8s enable storage
microk8s enable dns
microk8s enable metallb:$LOCAL_ADDRESS-$LOCAL_ADDRESS
</code></pre></div> </div>
<p>These commands enable</p>
<ul>
<li>the metrics server, which is helpful for further investigating your cluster usage</li>
<li>the storage module, which enables persistent volumes</li>
<li>the cluster dns server</li>
<li>and the load balancing module, which enables external access to your cluster</li>
</ul>
<p>In order to control the cluster using the Kubernetes CLI (<code class="language-plaintext highlighter-rouge">kubectl</code>) you can either use integrated option which ships with MicroK8s. Alternatively, e.g. if you want to control your cluster from another machine you can extract the cluster config file and use it with a native install of <code class="language-plaintext highlighter-rouge">kubectl</code>.</p>
<h4 id="integrated-option">Integrated option</h4>
<p>Use <code class="language-plaintext highlighter-rouge">microk8s kubectl &lt;command&gt;</code></p>
<h4 id="native-option">Native option</h4>
<p>Execute <code class="language-plaintext highlighter-rouge">microk8s kubectl config view --raw &gt; $HOME/.kube/config</code></p>
<p>Now, use <code class="language-plaintext highlighter-rouge">kubectl &lt;command&gt;</code></p>
<p>In case you are not running the cluster on a your local machine you can also enable remote access by following these steps:</p>
<ul>
<li>stop your cluster by running <code class="language-plaintext highlighter-rouge">microk8s stop</code></li>
<li>edit the file <code class="language-plaintext highlighter-rouge">/var/snap/microk8s/current/certs/csr.conf.template</code> and add your domain/ip address:
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>DNS.6 = &lt;domain_of_microk8s_host&gt;
IP.7 = &lt;public_ip_of_microk8s_host&gt;
</code></pre></div> </div>
<p>Restart your cluster by running <code class="language-plaintext highlighter-rouge">microk8s start</code> and extract the new Kubernetes configuration by executing <code class="language-plaintext highlighter-rouge">microk8s kubectl config view --raw</code>.
Now copy the configuration description to your local machine under <code class="language-plaintext highlighter-rouge">~/.kube/config</code>.</p>
</li>
</ul>
<h4 id="loadbalancing-and-ingress">Loadbalancing and Ingress</h4>
<p>Once you have a Kubernetes cluster available and installed the Kubernetes and Helm CLI (see below), you are now ready to setup an Eclipse IoT Packages deployment such as Cloud2Edge.
By default the containers can communicate within the cluster.
But to access a container and its respective services from outside the cluster (e.g. from another machine) you need to perform some extra steps.
Depending on your setup, you have 3 options available how to make your services externally available.</p>
<h5 id="nodeport">NodePort</h5>
<p>The simplest way to expose your services is by using a Kubernetes Service with type <a href="https://kubernetes.io/docs/concepts/services-networking/service/#nodeport">NodePort</a>. The Cloud2Edge Helm chart uses this option by default.
Using this in production mode has some drawbacks and is not recommended as it is very static, enables just one Service per port and only allows you to use ports in the range 30000–32767.</p>
<h5 id="loadbalancer">LoadBalancer</h5>
<p>Another option is the <a href="https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer">LoadBalancer</a> Service type, which requires your Kubernetes provider to supply an external load balancing module. The provider is also responsible for provisioning external IP addresses.
The Cloud2Edge enables this option by setting the flags <code class="language-plaintext highlighter-rouge">hono.useLoadBalancer</code> to <code class="language-plaintext highlighter-rouge">true</code> and <code class="language-plaintext highlighter-rouge">ditto.nginx.service.type</code> to <code class="language-plaintext highlighter-rouge">LoadBalancer</code>.
The downside of this approach is that each of your Services requires its own publicly available IP address, which might either not be desirable (due to higher costs) or possible at all e.g. in case you are running your deployment on a custom Kubernetes setup (such as MicroK8s).</p>
<h5 id="ingress-loadbalancing">Ingress Loadbalancing</h5>
<p>Ingress controllers employ a single LoadBalancer service, which then routes all incoming traffic to the actual controller in charge of distributing it to the right endpoints.
This allows not only differentiating by port, but also routing by path (HTTP) or subdomain.
We are using the <a href="https://www.getambassador.io/">Ambassador</a> controller, as it is capable of routing not only HTTP ingress but also on the TCP level.</p>
<ol>
<li>Deploy the Cloud2Edge Helm chart setting the aforementioned flags to <strong>not</strong> use the LoadBalancer type</li>
<li>Create a namespace for Ambassador: <code class="language-plaintext highlighter-rouge">kubectl create namespace ambassador</code></li>
<li>Add the Helm repository: <code class="language-plaintext highlighter-rouge">helm repo add datawire https://www.getambassador.io</code></li>
<li>Create a file named <code class="language-plaintext highlighter-rouge">override.yaml</code>, that contains all necessary Ambassador configurations:
```yaml
enableAES: false
image:
repository: docker.io/datawire/ambassador
replicaCount: 1
service:
ports:
<ul>
<li>name: mqtt-adapter
port: 1883
targetPort: 1883</li>
<li>name: http-adapter
port: 18080
targetPort: 18080</li>
<li>name: device-registry
port: 28080
targetPort: 28080</li>
<li>name: dispatch-router
port: 5671
targetPort: 15671</li>
<li>name: ditto
port: 38080
targetPort: 38080
```</li>
</ul>
</li>
<li>Start Ambassador by running <code class="language-plaintext highlighter-rouge">helm install ambassador -n ambassador -f override.yaml datawire/ambassador</code></li>
<li>Then we create the necessary mappings, such that the controller knows where to route incoming traffic. These mappings are specifically adjusted to the Cloud2Edge deployment, so you will have to create your own mappings for other packages. Create a new file named <code class="language-plaintext highlighter-rouge">ambassador-mappings.yaml</code> and replace the release name and the namespace of your deployment:
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">getambassador.io/v2</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">TCPMapping</span>
<span class="na">metadata</span><span class="pi">:</span>
<span class="na">name</span><span class="pi">:</span> <span class="s">ambassador-http-adapter</span>
<span class="na">namespace</span><span class="pi">:</span> <span class="s">ambassador</span>
<span class="na">spec</span><span class="pi">:</span>
<span class="na">port</span><span class="pi">:</span> <span class="m">18080</span>
<span class="na">service</span><span class="pi">:</span> <span class="pi">{</span><span class="nv">name-of-helm-release</span><span class="pi">}</span><span class="s">-adapter-http-vertx.{kubernetes-namespace}:8080</span>
<span class="nn">---</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">getambassador.io/v2</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">TCPMapping</span>
<span class="na">metadata</span><span class="pi">:</span>
<span class="na">name</span><span class="pi">:</span> <span class="s">ambassador-mqtt-adapter</span>
<span class="na">namespace</span><span class="pi">:</span> <span class="s">ambassador</span>
<span class="na">spec</span><span class="pi">:</span>
<span class="na">port</span><span class="pi">:</span> <span class="m">1883</span>
<span class="na">service</span><span class="pi">:</span> <span class="pi">{</span><span class="nv">name-of-helm-release</span><span class="pi">}</span><span class="s">-adapter-mqtt-vertx.{kubernetes-namespace}:1883</span>
<span class="nn">---</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">getambassador.io/v2</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">TCPMapping</span>
<span class="na">metadata</span><span class="pi">:</span>
<span class="na">name</span><span class="pi">:</span> <span class="s">ambassador-device-registry</span>
<span class="na">namespace</span><span class="pi">:</span> <span class="s">ambassador</span>
<span class="na">spec</span><span class="pi">:</span>
<span class="na">port</span><span class="pi">:</span> <span class="m">28080</span>
<span class="na">service</span><span class="pi">:</span> <span class="pi">{</span><span class="nv">name-of-helm-release</span><span class="pi">}</span><span class="s">-service-device-registry-ext.{kubernetes-namespace}:28080</span>
<span class="nn">---</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">getambassador.io/v2</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">TCPMapping</span>
<span class="na">metadata</span><span class="pi">:</span>
<span class="na">name</span><span class="pi">:</span> <span class="s">ambassador-dispatch-router</span>
<span class="na">namespace</span><span class="pi">:</span> <span class="s">ambassador</span>
<span class="na">spec</span><span class="pi">:</span>
<span class="na">port</span><span class="pi">:</span> <span class="m">15671</span>
<span class="na">service</span><span class="pi">:</span> <span class="pi">{</span><span class="nv">name-of-helm-release</span><span class="pi">}</span><span class="s">-service-device-registry-ext.{kubernetes-namespace}:15671</span>
<span class="nn">---</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">getambassador.io/v2</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">TCPMapping</span>
<span class="na">metadata</span><span class="pi">:</span>
<span class="na">name</span><span class="pi">:</span> <span class="s">ambassador-ditto</span>
<span class="na">namespace</span><span class="pi">:</span> <span class="s">ambassador</span>
<span class="na">spec</span><span class="pi">:</span>
<span class="na">port</span><span class="pi">:</span> <span class="m">38080</span>
<span class="na">service</span><span class="pi">:</span> <span class="pi">{</span><span class="nv">name-of-helm-release</span><span class="pi">}</span><span class="s">-ditto-nginx.{kubernetes-namespace}:8080</span>
</code></pre></div> </div>
</li>
<li>Now add the mappings to your cluster: <code class="language-plaintext highlighter-rouge">kubectl apply -f ambassador-mappings.yaml</code></li>
</ol>
<p>You are now able to access the deployed Cloud2Edge services externally. Check what IP address you are using by running <code class="language-plaintext highlighter-rouge">kubectl get service -n ambassador</code>.</p>
<p>Navigate to http://<your_ip>:38080 and check whether Ditto is running.</your_ip></p>
<h3 id="minikube">Minikube</h3>
<p><a href="https://kubernetes.io/docs/setup/learning-environment/minikube/">Minikube</a> is Kubernetes in a bottle.</p>
<p>Instead of provisioning a full blown cluster, it will create a virtual machine on your local system, and
provision a small, single-node cluster inside of it. As it puts the operating system in a VM, Minikube itself
can run on all major operating systems, including Windows and Mac OS.</p>
<p>Instead of duplicating the effort, documenting how to get Minikube up an running, we leave this to the
excellent <a href="https://kubernetes.io/docs/tasks/tools/install-minikube/">documentation of Minikube</a> itself.</p>
<h4 id="getting-started">Getting started</h4>
<p>Once you have everything installed, you should be able to start a new cluster by executing:</p>
<div class="clipboard"><pre><code>minikube start</code></pre></div>
<p>And you can switch <code class="language-plaintext highlighter-rouge">kubectl</code> to the context <code class="language-plaintext highlighter-rouge">minikube</code>, and interact with your cluster:</p>
<div class="clipboard"><pre><code>kubectl config use-context minikube</code></pre></div>
<p>For example, get the current version of the client and server:</p>
<div class="clipboard"><pre><code>kubectl version</code></pre></div>
<p>Which should show a proper version for the client <strong>and</strong> the server:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Client Version: version.Info{Major:"1", Minor:"11+", GitVersion:"v1.11.0+d4cacc0", GitCommit:"d4cacc0", GitTreeState:"clean", BuildDate:"2018-10-10T16:38:01Z", GoVersion:"go1.10.3", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"13+", GitVersion:"v1.13.4+c2a5caf", GitCommit:"c2a5caf", GitTreeState:"clean", BuildDate:"2019-09-21T02:12:52Z", GoVersion:"go1.11.13", Compiler:"gc", Platform:"linux/amd64"}
</code></pre></div> </div>
<h4 id="starting-and-stopping">Starting and stopping</h4>
<p>When you no longer need your cluster, you can stop it using:</p>
<div class="clipboard"><pre><code>minikube stop</code></pre></div>
<p>This will suspend the VM so that you can, later on, resume it by
executing:</p>
<div class="clipboard"><pre><code>minikube start</code></pre></div>
<p>Or delete it using:</p>
<div class="clipboard"><pre><code>minikube delete</code></pre></div>
<h4 id="resources">Resources</h4>
<p>You can translate the package resources requirements into arguments for the <code class="language-plaintext highlighter-rouge">start</code> command like this:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>minikube start --cpus &lt;cpus&gt; --disk-size &lt;size&gt; --memory &lt;memory&gt; --kubernetes-version &lt;version&gt;
</code></pre></div> </div>
<p>Using the following arguments:</p>
<dl class="row">
<dt class="col-sm-2">cpus</dt>
<dd class="col-sm-10">The number of CPUs you allocate for Minikube (e.g. <code>--cpus 2</code>).</dd>
<dt class="col-sm-2">size</dt>
<dd class="col-sm-10">
The size of the disk available for the cluster and persistent volumes. In the format <code>&lt;number&gt;&lt;unit&gt;</code>,
where unit can be either <code>k</code>, <code>m</code>, or <code>g</code> (e.g. 20GiB means <code>--disk-size 20g</code>).
</dd>
<dt class="col-sm-2">memory</dt>
<dd class="col-sm-10">
The amount of RAM allocated to the virtual machine. This is the amount in MiB (e.g. for 8GiB means <code>--memory 8192</code>.
</dd>
<dt class="col-sm-2">version</dt>
<dd class="col-sm-10">
The Kubernetes version deployed into the virtual machine (e.g. <code>--kubernetes-version v1.20.2</code>).
</dd>
</dl>
<h4 id="addons">Addons</h4>
<p>Some packages may require additional addons, like the ingress addon for providing a default ingress controllers.</p>
<p>Such addons can be enabled when starting the Minikube instance, using the following flag:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>minikube start ... --addons ingress
</code></pre></div> </div>
<h2 id="helm">Helm</h2>
<p>You will need an installation of Helm on the machine which is used to deploy the packages. You can find
installation instructions for Helm in the Helm documentation under <a href="https://helm.sh/docs/using_helm/#installing-helm">Installing Helm</a>.</p>
<p>The required Helm version is 3.1 or later.</p>
<h3 id="repository">Repository</h3>
<p>The Eclipse IoT Packages projects publishes a Helm chart repository for Eclipse IoT projects.</p>
<p>Adding the repository can be done on your local machine be executing:</p>
<div class="clipboard"><pre><code>helm repo add eclipse-iot https://eclipse.org/packages/charts</code></pre></div>
<p>Read more: <a href="/packages/repository" title="title">Helm chart repository</a>.</p>
<h2 id="command-line-tools">Command line tools</h2>
<p>Some tutorials might require some common command line tool. The installation depends on the
operating system you are using.</p>
<p>It is required to have the following tools installed:</p>
<h3 id="bash">Bash</h3>
<p>Bash is available on Windows, Linux and Mac OS X. True platform independence. So all commands which
you are supposed to execute can be executed in Bash, version 3 or newer.</p>
<h3 id="curl">curl</h3>
<p>For downloading files and execution API call the tool <code class="language-plaintext highlighter-rouge">curl</code> will be used.</p>
<h3 id="mosquitto-cli">Mosquitto CLI</h3>
<p>Mosquitto command line tools: e.g. <code class="language-plaintext highlighter-rouge">moquitto_pub</code></p>
</div>
<div class="col-12 col-md-4 col-lg-3
">
<div class="position-sticky" style="top: 4rem;">
<aside>
<div class="card">
<div class="card-body">
<h5 class="card-title">Table of contents</h5>
<div id="toc" class="toc"></div>
</div>
</div>
</aside>
<script>generateToc("#toc", "h2[id], h3[id], h4[id]");</script>
</div>
</div>
</div>
</div>
</main>
<footer class="page-footer">
<div class="container">
<div class="row">
<div class="col-6 col-md">
<h5>Eclipse IoT Packages</h5>
<ul class="list-unstyled">
<li>
<a class="text-muted" href="/packages/">Home</a>
</li>
<li>
<a class="text-muted" href="/packages/about">About</a>
</li>
<li>
<a class="text-muted" href="/packages/contribute">Contribute</a>
</li>
<li>
<a class="text-muted" href="https://eclipse.org/security">Security</a>
</li>
</ul>
</div>
<div class="col-6 col-md">
<h5>Eclipse IoT</h5>
<ul class="list-unstyled">
<li>
<a class="text-muted" href="https://iot.eclipse.org">Top Level Project</a>
</li>
<li>
<a class="text-muted" href="https://www.eclipse.org/org/workinggroups/iotwg_charter.php">Working group</a>
</li>
</ul>
</div>
<div class="col-6 col-md">
<h5>Legal</h5>
<ul class="list-unstyled">
<li>
<a class="text-muted" href="https://www.eclipse.org/legal/privacy.php">Privacy Policy</a>
</li>
<li>
<a class="text-muted" href="https://www.eclipse.org/legal/termsofuse.php">Terms of Use</a>
</li>
<li>
<a class="text-muted" href="https://www.eclipse.org/legal/copyright.php">Copyright Agent</a>
</li>
<li>
<a class="text-muted" href="https://www.eclipse.org/legal/epl-2.0/">Eclipse Public License</a>
</li>
<li>
<a class="text-muted" href="https://www.eclipse.org/legal">Legal Resources</a>
</li>
</ul>
</div>
<div class="col-6 col-md">
<h5>Eclipse Foundation</h5>
<ul class="list-unstyled">
<li>
<a class="text-muted" href="https://eclipse.org/org">About us</a>
</li>
<li>
<a class="text-muted" href="https://www.eclipse.org/org/foundation/contact.php">Contact us</a>
</li>
<li>
<a class="text-muted" href="https://www.eclipse.org/org/foundation/directors.php">Board of Directors</a>
</li>
</ul>
</div>
</div>
<div class="row">
<div class="col-12 col-md text-muted">Copyright © Eclipse Foundation, Inc. All Rights Reserved.</div>
</div>
</div>
</footer>
<script src="/packages/assets/popper.min.js"></script>
<script src="/packages/assets/jquery.min.js"></script>
<script src="/packages/assets/bootstrap/bootstrap.min.js"></script>
<script src="/packages/assets/clipboard.min.js"></script>
<script src="/packages/assets/clipboard.js"></script>
</body>
</html>