mirror of https://github.com/docker/docs.git
Rework extension backend doc (#16236)
* Genericize frontend tutorial and make it clear other tech are usable and track it * Rework backend tutorial to add tabs for other techs and incorporate the build and install commands. * Guillaume's feedback
This commit is contained in:
parent
4c8c02fe4b
commit
90ca64879c
|
@ -891,7 +891,7 @@ reference:
|
|||
- path: /desktop/extensions-sdk/dev/api/reference/interfaces/ExecResultV0/
|
||||
title: ExecResultV0
|
||||
- path: /desktop/extensions-sdk/dev/api/reference/interfaces/BackendV0/
|
||||
title: BackendV0
|
||||
title: BackendV0
|
||||
- title: Dockerfile reference
|
||||
path: /engine/reference/builder/
|
||||
- sectiontitle: Compose file reference
|
||||
|
@ -1179,14 +1179,14 @@ manuals:
|
|||
title: Quickstart
|
||||
- sectiontitle: "Part one: Build"
|
||||
section:
|
||||
- sectiontitle: "Step one: Set up..."
|
||||
- sectiontitle: "Step one: Create a new extension"
|
||||
section:
|
||||
- title: ...a minimal frontend extension
|
||||
- title: Create a simple extension
|
||||
path: /desktop/extensions-sdk/build/set-up/minimal-frontend-extension/
|
||||
- title: ... an advanced frontend extension
|
||||
- title: Create a more advanced frontend
|
||||
path: /desktop/extensions-sdk/build/set-up/frontend-extension-tutorial/
|
||||
- title: ... a minimal backend extension
|
||||
path: /desktop/extensions-sdk/build/set-up/minimal-backend-extension/
|
||||
- title: Add a backend to your extension
|
||||
path: /desktop/extensions-sdk/build/set-up/backend-extension-tutorial/
|
||||
- title: "Step two: Build and install"
|
||||
path: /desktop/extensions-sdk/build/build-install/
|
||||
- title: "Step three: Test and debug"
|
||||
|
|
|
@ -0,0 +1,420 @@
|
|||
---
|
||||
title: Add a backend to your extension
|
||||
description: Learn how to add a backend to your extension.
|
||||
keywords: Docker, extensions, sdk, build
|
||||
redirect_from:
|
||||
- /desktop/extensions-sdk/tutorials/minimal-backend-extension/
|
||||
- /desktop/extensions-sdk/build/minimal-backend-extension/
|
||||
---
|
||||
|
||||
Your extension can ship a backend part with which the frontend can interact with. This page provides information on
|
||||
why and how to add a backend.
|
||||
|
||||
> Note
|
||||
>
|
||||
> Before you start, make sure you have installed the latest version of [Docker Desktop](https://www.docker.com/products/docker-desktop/).
|
||||
|
||||
> Note
|
||||
>
|
||||
> If you want to start a codebase for your new extension, our [Quickstart guide](../../quickstart.md) and `docker extension init <my-extension>` provides a better base for your extension as it is more up-to-date and related to your install of Docker Desktop.
|
||||
|
||||
## Why add a backend?
|
||||
|
||||
Thanks to the Docker Extensions SDK, most of the time you should be able to do what you need from the Docker CLI
|
||||
directly from [the frontend](./frontend-extension-tutorial.md#use-the-extension-apis-client).
|
||||
|
||||
Nonetheless, there are some cases where you might need to add a backend to your extension. So far, extension
|
||||
builders have used the backend to:
|
||||
- Store data in a local database and serve them back with a REST API.
|
||||
- Store the extension state, like when a button starts a long-running process, so that if you navigate away
|
||||
from the extension user interface and come back, the frontend can pick up where it left off.
|
||||
|
||||
## Add a backend to the extension
|
||||
|
||||
If you created your extension using the `docker extension init` command, you already have a backend set up. If it is
|
||||
not the case, then you have to first create a `vm` directory that will contain the code and update the Dockerfile to
|
||||
containerize it.
|
||||
|
||||
Here is the extension folder structure with a backend:
|
||||
|
||||
```bash
|
||||
.
|
||||
├── Dockerfile # (1)
|
||||
├── Makefile
|
||||
├── metadata.json
|
||||
├── ui
|
||||
└── index.html
|
||||
└── vm # (2)
|
||||
├── go.mod
|
||||
└── main.go
|
||||
```
|
||||
|
||||
1. Contains everything required to build the backend and copy it in the extension's container filesystem.
|
||||
2. The source folder that contains the backend code of the extension
|
||||
|
||||
Although you can start from an empty directory or from the `vm-ui extension` [sample](https://github.com/docker/extensions-sdk/tree/main/samples){:target="_blank" rel="noopener" class="_"},
|
||||
it is highly recommended that you start from the `docker extension init` command and change it to suit your needs.
|
||||
|
||||
> **Tip**
|
||||
>
|
||||
> The `docker extension init` generates a Go backend. But you can still use it as a starting point for
|
||||
> your own extension and use any other language like Node.js, Python, Java, .Net, or any other language and framework.
|
||||
{: .tip }
|
||||
|
||||
On this tutorial, the backend service simply exposes one route that returns a JSON payload that says "Hello".
|
||||
|
||||
```json
|
||||
{ "Message": "Hello" }
|
||||
```
|
||||
|
||||
> **Important**
|
||||
>
|
||||
> We recommend that, the frontend and the backend communicate through sockets (and named pipes on Windows) instead of
|
||||
> HTTP. On one hand, because it will prevent port collision with any other running application or container running
|
||||
> on the host. On the other hand, because some Docker Desktop users are running in constrained environments where they
|
||||
> can't open ports on their machines. So, when choosing the language and framework for your backend, make sure it
|
||||
> supports sockets connection.
|
||||
{: .important}
|
||||
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active"><a data-toggle="tab" data-target="#go-app" data-group="go">Go</a></li>
|
||||
<li><a data-toggle="tab" data-target="#node-app" data-group="node">Node</a></li>
|
||||
<li><a data-toggle="tab" data-target="#python-app" data-group="python">Python</a></li>
|
||||
<li><a data-toggle="tab" data-target="#java-app" data-group="java">Java</a></li>
|
||||
<li><a data-toggle="tab" data-target="#net-app" data-group="net">.Net</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div id="go-app" class="tab-pane fade in active" markdown="1">
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/labstack/echo"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var socketPath string
|
||||
flag.StringVar(&socketPath, "socket", "/run/guest/volumes-service.sock", "Unix domain socket to listen on")
|
||||
flag.Parse()
|
||||
|
||||
os.RemoveAll(socketPath)
|
||||
|
||||
logrus.New().Infof("Starting listening on %s\n", socketPath)
|
||||
router := echo.New()
|
||||
router.HideBanner = true
|
||||
|
||||
startURL := ""
|
||||
|
||||
ln, err := listen(socketPath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
router.Listener = ln
|
||||
|
||||
router.GET("/hello", hello)
|
||||
|
||||
log.Fatal(router.Start(startURL))
|
||||
}
|
||||
|
||||
func listen(path string) (net.Listener, error) {
|
||||
return net.Listen("unix", path)
|
||||
}
|
||||
|
||||
func hello(ctx echo.Context) error {
|
||||
return ctx.JSON(http.StatusOK, HTTPMessageBody{Message: "hello world"})
|
||||
}
|
||||
|
||||
type HTTPMessageBody struct {
|
||||
Message string
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
<div id="node-app" class="tab-pane fade" markdown="1">
|
||||
|
||||
<br/>
|
||||
|
||||
> **Important**
|
||||
>
|
||||
> We don't have a working example for Node yet. [Fill out the form](https://docs.google.com/forms/d/e/1FAIpQLSdxJDGFJl5oJ06rG7uqtw1rsSBZpUhv_s9HHtw80cytkh2X-Q/viewform?usp=pp_url&entry.25798127=Node)
|
||||
> and let us know you'd like a sample for Node.
|
||||
{: .important }
|
||||
|
||||
</div>
|
||||
<div id="python-app" class="tab-pane fade" markdown="1">
|
||||
|
||||
<br/>
|
||||
|
||||
> **Important**
|
||||
>
|
||||
> We don't have a working example for Python yet. [Fill out the form](https://docs.google.com/forms/d/e/1FAIpQLSdxJDGFJl5oJ06rG7uqtw1rsSBZpUhv_s9HHtw80cytkh2X-Q/viewform?usp=pp_url&entry.25798127=Python)
|
||||
> and let us know you'd like a sample for Python.
|
||||
{: .important }
|
||||
|
||||
</div>
|
||||
<div id="java-app" class="tab-pane fade" markdown="1">
|
||||
|
||||
<br/>
|
||||
|
||||
> **Important**
|
||||
>
|
||||
> We don't have a working example for Java yet. [Fill out the form](https://docs.google.com/forms/d/e/1FAIpQLSdxJDGFJl5oJ06rG7uqtw1rsSBZpUhv_s9HHtw80cytkh2X-Q/viewform?usp=pp_url&entry.25798127=Java)
|
||||
> and let us know you'd like a sample for Java.
|
||||
{: .important }
|
||||
|
||||
</div>
|
||||
<div id="net-app" class="tab-pane fade" markdown="1">
|
||||
|
||||
<br/>
|
||||
|
||||
> **Important**
|
||||
>
|
||||
> We don't have a working example for .Net. [Fill out the form](https://docs.google.com/forms/d/e/1FAIpQLSdxJDGFJl5oJ06rG7uqtw1rsSBZpUhv_s9HHtw80cytkh2X-Q/viewform?usp=pp_url&entry.25798127=.Net)
|
||||
> and let us know you'd like a sample for .Net.
|
||||
{: .important }
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
## Adapt the Dockerfile
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> When using the `docker extension init`, it creates a `Dockerfile` that already contains what is needed for a Go backend.
|
||||
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active"><a data-toggle="tab" data-target="#go-dockerfile" data-group="go">For Go</a></li>
|
||||
<li><a data-toggle="tab" data-target="#node-dockerfile" data-group="node">For Node</a></li>
|
||||
<li><a data-toggle="tab" data-target="#python-dockerfile" data-group="python">For Python</a></li>
|
||||
<li><a data-toggle="tab" data-target="#java-dockerfile" data-group="java">For Java</a></li>
|
||||
<li><a data-toggle="tab" data-target="#net-dockerfile" data-group="net">.For Net</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div id="go-dockerfile" class="tab-pane fade in active" markdown="1">
|
||||
|
||||
<br/>
|
||||
|
||||
To deploy your Go backend when installing the extension, you need first to configure the `Dockerfile` so that:
|
||||
- it builds the backend application
|
||||
- it copies the binary in the extension's container filesystem
|
||||
- it starts the binary when the container starts listening on the extension socket
|
||||
|
||||
```dockerfile
|
||||
FROM node:17.7-alpine3.14 AS client-builder
|
||||
# ... build frontend application
|
||||
|
||||
# Build the Go backend
|
||||
FROM golang:1.17-alpine AS builder
|
||||
ENV CGO_ENABLED=0
|
||||
WORKDIR /backend
|
||||
COPY vm/go.* .
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
--mount=type=cache,target=/root/.cache/go-build \
|
||||
go mod download
|
||||
COPY vm/. .
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
--mount=type=cache,target=/root/.cache/go-build \
|
||||
go build -trimpath -ldflags="-s -w" -o bin/service
|
||||
|
||||
FROM alpine:3.15
|
||||
# ... add labels and copy the frontend application
|
||||
|
||||
COPY --from=builder /backend/bin/service /
|
||||
CMD /service -socket /run/guest-services/extension-allthethings-extension.sock
|
||||
```
|
||||
|
||||
</div>
|
||||
<div id="node-dockerfile" class="tab-pane fade" markdown="1">
|
||||
|
||||
<br/>
|
||||
|
||||
> **Important**
|
||||
>
|
||||
> We don't have a working Dockerfile for Node yet. [Fill out the form](https://docs.google.com/forms/d/e/1FAIpQLSdxJDGFJl5oJ06rG7uqtw1rsSBZpUhv_s9HHtw80cytkh2X-Q/viewform?usp=pp_url&entry.25798127=Node)
|
||||
> and let us know you'd like a Dockerfile for Node.
|
||||
{: .important }
|
||||
|
||||
</div>
|
||||
<div id="python-dockerfile" class="tab-pane fade" markdown="1">
|
||||
|
||||
<br/>
|
||||
|
||||
> **Important**
|
||||
>
|
||||
> We don't have a working Dockerfile for Python yet. [Fill out the form](https://docs.google.com/forms/d/e/1FAIpQLSdxJDGFJl5oJ06rG7uqtw1rsSBZpUhv_s9HHtw80cytkh2X-Q/viewform?usp=pp_url&entry.25798127=Python)
|
||||
> and let us know you'd like a Dockerfile for Python.
|
||||
{: .important }
|
||||
|
||||
</div>
|
||||
<div id="java-dockerfile" class="tab-pane fade" markdown="1">
|
||||
|
||||
<br/>
|
||||
|
||||
> **Important**
|
||||
>
|
||||
> We don't have a working Dockerfile for Java yet. [Fill out the form](https://docs.google.com/forms/d/e/1FAIpQLSdxJDGFJl5oJ06rG7uqtw1rsSBZpUhv_s9HHtw80cytkh2X-Q/viewform?usp=pp_url&entry.25798127=Java)
|
||||
> and let us know you'd like a Dockerfile for Java.
|
||||
{: .important }
|
||||
|
||||
</div>
|
||||
<div id="net-dockerfile" class="tab-pane fade" markdown="1">
|
||||
|
||||
<br/>
|
||||
|
||||
> **Important**
|
||||
>
|
||||
> We don't have a working Dockerfile for .Net. [Fill out the form](https://docs.google.com/forms/d/e/1FAIpQLSdxJDGFJl5oJ06rG7uqtw1rsSBZpUhv_s9HHtw80cytkh2X-Q/viewform?usp=pp_url&entry.25798127=.Net)
|
||||
> and let us know you'd like a Dockerfile for .Net.
|
||||
{: .important }
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
## Configure the metadata file
|
||||
|
||||
To start the backend service of your extension inside the VM of Docker Desktop, you have to configure the image name
|
||||
in the `vm` section of the `metadata.json` file.
|
||||
|
||||
```json
|
||||
{
|
||||
"vm": {
|
||||
"image": "${DESKTOP_PLUGIN_IMAGE}"
|
||||
},
|
||||
"icon": "docker.svg",
|
||||
"ui": {
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For more information on the `vm` section of the `metadata.json`, see [Metadata](../../extensions/METADATA.md).
|
||||
|
||||
> **Warning**
|
||||
>
|
||||
> Do not replace the `${DESKTOP_PLUGIN_IMAGE}` placeholder in the `metadata.json` file. The placeholder is replaced automatically with the correct image name when the extension is installed.
|
||||
{: .warning}
|
||||
|
||||
## Invoke the extension backend from your frontend
|
||||
|
||||
Using the [advanced frontend extension example](./frontend-extension-tutorial.md), we can invoke our extension backend.
|
||||
|
||||
Use the Docker Desktop Client object and then invoke the `/hello` route from the backend service with `ddClient.
|
||||
extension.vm.service.get` that returns the body of the response.
|
||||
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active"><a data-toggle="tab" data-target="#react-app" data-group="react">React</a></li>
|
||||
<li><a data-toggle="tab" data-target="#vue-app" data-group="vue">Vue</a></li>
|
||||
<li><a data-toggle="tab" data-target="#angular-app" data-group="angular">Angular</a></li>
|
||||
<li><a data-toggle="tab" data-target="#svelte-app" data-group="svelte">Svelte</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div id="react-app" class="tab-pane fade in active" markdown="1">
|
||||
|
||||
Replace the `ui/src/App.tsx` file with the following code:
|
||||
|
||||
```tsx
|
||||
{% raw %}
|
||||
// ui/src/App.tsx
|
||||
import React, { useEffect } from 'react';
|
||||
import { createDockerDesktopClient } from "@docker/extension-api-client";
|
||||
|
||||
//obtain docker destkop extension client
|
||||
const ddClient = createDockerDesktopClient();
|
||||
|
||||
export function App() {
|
||||
const ddClient = createDockerDesktopClient();
|
||||
const [hello, setHello] = useState<string>();
|
||||
|
||||
useEffect(() => {
|
||||
const getHello = async () => {
|
||||
const result = await ddClient.extension.vm?.service?.get('/hello');
|
||||
setHello(JSON.stringify(result));
|
||||
}
|
||||
getHello()
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Typography>{hello}</Typography>
|
||||
);
|
||||
}
|
||||
{% endraw %}
|
||||
```
|
||||
|
||||
</div>
|
||||
<div id="vue-app" class="tab-pane fade" markdown="1">
|
||||
|
||||
<br/>
|
||||
|
||||
> **Important**
|
||||
>
|
||||
> We don't have an example for Vue yet. [Fill out the form](https://docs.google.com/forms/d/e/1FAIpQLSdxJDGFJl5oJ06rG7uqtw1rsSBZpUhv_s9HHtw80cytkh2X-Q/viewform?usp=pp_url&entry.1333218187=Vue)
|
||||
> and let us know you'd like a sample with Vue.
|
||||
{: .important }
|
||||
|
||||
</div>
|
||||
<div id="angular-app" class="tab-pane fade" markdown="1">
|
||||
|
||||
<br/>
|
||||
|
||||
> **Important**
|
||||
>
|
||||
> We don't have an example for Angular yet. [Fill out the form](https://docs.google.com/forms/d/e/1FAIpQLSdxJDGFJl5oJ06rG7uqtw1rsSBZpUhv_s9HHtw80cytkh2X-Q/viewform?usp=pp_url&entry.1333218187=Angular)
|
||||
> and let us know you'd like a sample with Angular.
|
||||
{: .important }
|
||||
|
||||
</div>
|
||||
<div id="svelte-app" class="tab-pane fade" markdown="1">
|
||||
|
||||
<br/>
|
||||
|
||||
> **Important**
|
||||
>
|
||||
> We don't have an example for Svelte yet. [Fill out the form](https://docs.google.com/forms/d/e/1FAIpQLSdxJDGFJl5oJ06rG7uqtw1rsSBZpUhv_s9HHtw80cytkh2X-Q/viewform?usp=pp_url&entry.1333218187=Svelte)
|
||||
> and let us know you'd like a sample with Svelte.
|
||||
{: .important }
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
## Re-build the extension and update it
|
||||
|
||||
Since you have modified the configuration of the extension and added a stage in the Dockerfile, you must build again
|
||||
the extension.
|
||||
|
||||
```bash
|
||||
docker build --tag= awesome-inc/my-extension:latest .
|
||||
```
|
||||
|
||||
Once built, you need to update it (or install it if you haven't done it yet).
|
||||
|
||||
```bash
|
||||
docker extension update awesome-inc/my-extension:latest
|
||||
```
|
||||
|
||||
Now you can see the backend service running in the containers tab of the Docker Desktop Dashboard and watch the logs
|
||||
when you need to debug it.
|
||||
|
||||
> **Tip**
|
||||
>
|
||||
> You may need to enable the "Show system containers" option in Docker Desktop to see the backend container running
|
||||
> under the extension compose project in the containers tab of the dashboard.
|
||||
> See [how to show extension containers](../test-debug.md#show-the-extension-containers) for more information.
|
||||
{: .tip }
|
||||
|
||||
Open Docker Desktop Dashboard and click on the containers tab. You should see the response from the backend service
|
||||
call displayed.
|
||||
|
||||
## What's next?
|
||||
|
||||
Learn how to [share and publish your extension](../../extensions/index.md).
|
|
@ -142,7 +142,8 @@ COPY --from=client-builder /ui/build ui
|
|||
|
||||
## Configure the metadata file
|
||||
|
||||
A `metadata.json` file is required at the root of your extension directory.
|
||||
In order to add a tab in Docker Desktop for your extension, you have to configure it in the `metadata.json`
|
||||
file the root of your extension directory.
|
||||
|
||||
```json
|
||||
{
|
||||
|
@ -157,9 +158,34 @@ A `metadata.json` file is required at the root of your extension directory.
|
|||
}
|
||||
```
|
||||
|
||||
The `title` property is the name of the extension that is displayed in the left-menu of the Docker Desktop Dashboard.
|
||||
The `root` property is the path to the frontend application in the extension's container filesystem used by the
|
||||
system to deploy it on the host.
|
||||
The `src` property is the path to the HTML entry point of the frontend application within the `root` folder.
|
||||
|
||||
For more information on the `ui` section of hte `metadata.json`, see [Metadata](../../extensions/METADATA.md#ui-section).
|
||||
|
||||
## Build the extension and install it
|
||||
|
||||
Now that you have configured the extension, you need to build the extension image that Docker Desktop will use to
|
||||
install it.
|
||||
|
||||
```bash
|
||||
docker build --tag= awesome-inc/my-extension:latest .
|
||||
```
|
||||
|
||||
This built an image tagged `awesome-inc/my-extension:latest`, you can run `docker inspect
|
||||
awesome-inc/my-extension:latest` to see more details about it.
|
||||
|
||||
Finally, you can install the extension and see it appearing in the Docker Desktop Dashboard.
|
||||
|
||||
```bash
|
||||
docker extension install awesome-inc/my-extension:latest
|
||||
```
|
||||
|
||||
## Use the Extension APIs client
|
||||
|
||||
To use the Extension APIs and perform actions with Docker Desktop, the extension must first import the
|
||||
To use the Extension APIs and perform actions with Docker Desktop, the extension must first import the
|
||||
`@docker/extension-api-client` library. To install it, run the command below:
|
||||
|
||||
```bash
|
||||
|
@ -174,7 +200,7 @@ import { createDockerDesktopClient } from '@docker/extension-api-client';
|
|||
const ddClient = createDockerDesktopClient();
|
||||
```
|
||||
|
||||
When using Typescript, you can also install `@docker/extension-api-client-types` as a dev dependency. This will
|
||||
When using Typescript, you can also install `@docker/extension-api-client-types` as a dev dependency. This will
|
||||
provide you with type definitions for the extension APIs and auto-completion in your IDE.
|
||||
|
||||
```bash
|
||||
|
@ -183,7 +209,7 @@ npm install @docker/extension-api-client-types --save-dev
|
|||
|
||||

|
||||
|
||||
For example, you can use the `docker.cli.exec` function to get the list of all the containers via the `docker ps --all`
|
||||
For example, you can use the `docker.cli.exec` function to get the list of all the containers via the `docker ps --all`
|
||||
command and display the result in a table.
|
||||
|
||||
<ul class="nav nav-tabs">
|
||||
|
|
|
@ -1,129 +0,0 @@
|
|||
---
|
||||
title: Set up a minimal backend extension
|
||||
description: Minimal backend extension tutorial
|
||||
keywords: Docker, extensions, sdk, build
|
||||
redirect_from:
|
||||
- /desktop/extensions-sdk/tutorials/minimal-backend-extension/
|
||||
---
|
||||
|
||||
To start creating your extension, you first need a directory with files which range from the extension’s source code to the required extension-specific files. This page provides information on how to set up a simple Docker Extension that runs CLI commands in the backend.
|
||||
|
||||
For extensions with a backend service running REST services over sockets or named pipes, see the `vm-ui extension` [sample](https://github.com/docker/extensions-sdk/tree/main/samples){:target="_blank" rel="noopener" class="_"}.
|
||||
|
||||
> Note
|
||||
>
|
||||
> Before you start, make sure you have installed the latest version of [Docker Desktop](https://www.docker.com/products/docker-desktop/).
|
||||
|
||||
> Note
|
||||
>
|
||||
> If you want to start a codebase for your new extension, our [Quickstart guide](../../quickstart.md) and `docker extension init <my-extension>` provides a better base for your extension as it is more up-to-date and related to your install of Docker Desktop.
|
||||
|
||||
## Extension folder structure
|
||||
|
||||
In the `minimal-backend` [sample folder](https://github.com/docker/extensions-sdk/tree/main/samples){:target="_blank" rel="noopener" class="_"}, you can find a ready-to-go example that represents a UI extension built on HTML that runs a backend service. We will go through this code example in this tutorial.
|
||||
|
||||
Although you can start from an empty directory, it is highly recommended that you start from the template below and change it accordingly to suit your needs.
|
||||
|
||||
```bash
|
||||
.
|
||||
├── Dockerfile # (1)
|
||||
├── Makefile
|
||||
├── client # (2)
|
||||
│ └── src
|
||||
│ ├── App.tsx
|
||||
│ └── ... React aplication
|
||||
├── hello.sh # (3)
|
||||
└── metadata.json # (4)
|
||||
└── ui # (5)
|
||||
└── index.html
|
||||
```
|
||||
|
||||
1. Contains everything required to build the extension and run it in Docker Desktop.
|
||||
2. The source folder that contains the UI application. In this example we use a React frontend, the main part of th extension is an App.tsx.
|
||||
3. The script that runs inside the container.
|
||||
4. A file that provides information about the extension such as the name, description, and version.
|
||||
5. The source folder that contains all your HTML, CSS and JS files. There can also be other static assets such as logos and icons. For more information and guidelines on building the UI, see the [Design and UI styling section](../../design/design-guidelines.md).
|
||||
|
||||
If you want to set up user authentication for the extension, see [Authentication](../../guides/oauth2-flow.md).
|
||||
|
||||
## Invoke the extension backend from your javascript code
|
||||
|
||||
Using the [advanced frontend extension example](./frontend-extension-tutorial.md), we can invoke our extension backend from the `App.tsx` file.
|
||||
|
||||
Use the Docker Desktop Client object and then invoke a binary provided in our backend container (that lives inside the Docker Desktop VM) with `ddClient.docker.extension.vm.cli.exec()`.
|
||||
In our example, our hello.sh script returns a string as result, we obtain it with `result?.stdout`.
|
||||
|
||||
```typescript
|
||||
const ddClient = createDockerDesktopClient();
|
||||
const [backendInfo, setBackendInfo] = useState<string | undefined>();
|
||||
|
||||
async function runExtensionBackend(inputText: string) {
|
||||
const result = await ddClient.extension.vm?.cli.exec("./hello.sh", [
|
||||
inputText,
|
||||
]);
|
||||
setBackendInfo(result?.stdout);
|
||||
}
|
||||
```
|
||||
|
||||
## Create a Dockerfile
|
||||
|
||||
At minimum, your Dockerfile needs:
|
||||
|
||||
- Labels which provide extra information about the extension.
|
||||
- The source code which in this case is an `index.html` that sits within the `ui` folder. `index.html` refers to javascript code in `script.js`.
|
||||
- The `metadata.json` file.
|
||||
|
||||
```Dockerfile
|
||||
FROM node:17.7-alpine3.14 AS client-builder
|
||||
# ... build React application
|
||||
|
||||
FROM alpine:3.15
|
||||
|
||||
LABEL org.opencontainers.image.title="HelloBackend" \
|
||||
org.opencontainers.image.description="A sample extension that runs a shell script inside a container's Desktop VM." \
|
||||
org.opencontainers.image.vendor="Docker Inc." \
|
||||
com.docker.desktop.extension.api.version="1.0.0-beta.1" \
|
||||
com.docker.desktop.extension.icon="https://www.docker.com/wp-content/uploads/2022/03/Moby-logo.png"
|
||||
|
||||
COPY hello.sh .
|
||||
COPY metadata.json .
|
||||
COPY --from=client-builder /app/client/dist ui
|
||||
|
||||
CMD [ "sleep", "infinity" ]
|
||||
```
|
||||
|
||||
> **Tip**
|
||||
>
|
||||
> Enable the "Show system containers" option in Docker Desktop to see the extension container running.
|
||||
> See [how to show extension containers](../test-debug.md#show-the-extension-containers) for more information.
|
||||
{: .tip }
|
||||
|
||||
## Configure the metadata file
|
||||
|
||||
A `metadata.json` file is required at the root of the image filesystem.
|
||||
|
||||
```json
|
||||
{
|
||||
"vm": {
|
||||
"image": "${DESKTOP_PLUGIN_IMAGE}"
|
||||
},
|
||||
"ui": {
|
||||
"dashboard-tab": {
|
||||
"title": "Hello Backend Extension",
|
||||
"root": "/ui",
|
||||
"src": "index.html"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For more information on the `metadata.json`, see [Metadata](../../extensions/METADATA.md).
|
||||
|
||||
> **Warning**
|
||||
>
|
||||
> Do not replace the `${DESKTOP_PLUGIN_IMAGE}` placeholder in the `metadata.json` file. The placeholder is replaced automatically with the correct image name when the extension is installed.
|
||||
{: .warning}
|
||||
|
||||
## What's next?
|
||||
|
||||
Learn how to [build and install your extension](../build-install.md).
|
|
@ -32,9 +32,8 @@ Although you can start from an empty directory, it is highly recommended that yo
|
|||
|
||||
1. Contains everything required to build the extension and run it in Docker Desktop.
|
||||
2. A file that provides information about the extension such as the name, description, and version.
|
||||
3. The source folder that contains all your HTML, CSS and JS files. There can also be other static assets such as logos and icons. For more information and guidelines on building the UI, see the [Design and UI styling section](../../design/design-guidelines.md).
|
||||
|
||||
If you want to set up user authentication for the extension, see [Authentication](../../guides/oauth2-flow.md).
|
||||
3. The source folder that contains all your HTML, CSS and JS files. There can also be other static assets such as logos
|
||||
and icons. For more information and guidelines on building the UI, see the [Design and UI styling section](../../design/design-guidelines.md).
|
||||
|
||||
## Create a Dockerfile
|
||||
|
||||
|
|
Loading…
Reference in New Issue