Skip to content

Commit

Permalink
Prepare secret storage host binary for 0.2.7 release
Browse files Browse the repository at this point in the history
  • Loading branch information
ColinMcNeil committed Mar 3, 2025
1 parent 69e224e commit d9f40aa
Showing 7 changed files with 210 additions and 86 deletions.
4 changes: 2 additions & 2 deletions src/extension/Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
IMAGE?=docker/labs-ai-tools-for-devs
TAG?=0.1.4
TAG?=0.2.7

BUILDER=buildx-multi-arch

@@ -14,7 +14,7 @@ cross:
cd host-binary && $(MAKE) cross

build-extension: cross ## Build service image to be deployed as a desktop extension
docker build --platform=linux/amd64,linux/arm64,darwin/amd64,darwin/arm64,windows/amd64,windows/arm64 --tag=$(IMAGE):$(TAG) .
docker buildx build --load --platform=linux/amd64,linux/arm64,darwin/amd64,darwin/arm64,windows/amd64,windows/arm64 --tag=$(IMAGE):$(TAG) .

install-extension: build-extension ## Install the extension
docker extension install $(IMAGE):$(TAG)
33 changes: 29 additions & 4 deletions src/extension/host-binary/cmd/main.go
Original file line number Diff line number Diff line change
@@ -2,14 +2,14 @@ package main

import (
"context"
"encoding/json"
"fmt"
"github.com/docker/labs-ai-tools-for-devs/pkg/client"
secretsapi "github.com/docker/labs-ai-tools-for-devs/pkg/generated/go/client/secrets"
"github.com/docker/labs-ai-tools-for-devs/pkg/paths"
"os"
"os/signal"
"syscall"

secretsapi "github.com/docker/labs-ai-tools-for-devs/pkg/generated/go/client/secrets"
"github.com/docker/labs-ai-tools-for-devs/pkg/client"
"github.com/docker/labs-ai-tools-for-devs/pkg/paths"
"github.com/spf13/cobra"
)

@@ -18,6 +18,7 @@ func main() {
defer closeFunc()
paths.Init(paths.OnHost)
cmd := AddSecret(ctx)
cmd.AddCommand(ListSecrets(ctx))
if err := cmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
@@ -66,6 +67,18 @@ func AddSecret(ctx context.Context) *cobra.Command {
return cmd
}

func ListSecrets(ctx context.Context) *cobra.Command {
cmd := &cobra.Command{
Use: "list",
Short: "List all secrets",
Args: cobra.NoArgs,
RunE: func(*cobra.Command, []string) error {
return runListSecrets(ctx)
},
}
return cmd
}

const mcpPolicyName = "MCP"

func runAddSecret(ctx context.Context, opts addOptions) error {
@@ -79,6 +92,18 @@ func runAddSecret(ctx context.Context, opts addOptions) error {
return c.SetSecret(ctx, secretsapi.Secret{Name: opts.Name, Value: opts.Value, Policies: []string{mcpPolicyName}})
}

func runListSecrets(ctx context.Context) error {
c, err := newApiClient()
if err != nil {
return err
}
secrets, err := c.ListSecrets(ctx)
if err != nil {
return err
}
return json.NewEncoder(os.Stdout).Encode(secrets)
}

func assertMcpPolicyExists(ctx context.Context, apiClient client.ApiClient) error {
return apiClient.SetPolicy(ctx, secretsapi.Policy{Name: mcpPolicyName, Images: []string{"*"}})
}
6 changes: 3 additions & 3 deletions src/extension/metadata.json
Original file line number Diff line number Diff line change
@@ -18,17 +18,17 @@
{
"darwin": [
{
"path": "/darwin/api-client"
"path": "/darwin/host-binary"
}
],
"linux": [
{
"path": "/linux/api-client"
"path": "/linux/host-binary"
}
],
"windows": [
{
"path": "/windows/api-client.exe"
"path": "/windows/host-binary.exe"
}
]
}
44 changes: 44 additions & 0 deletions src/extension/ui/src/Secrets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// From secrets.yaml

import { v1 } from "@docker/extension-api-client-types";
import { CatalogItemWithName } from "./components/PromptCard";

namespace Secrets {
export type Secret = {
name: string;
value: string;
policies: string[];
}

export type StoredSecret = {
name: string;
policies: string[];
}

export type Policy = {
name: string;
images: string[];
}

export async function getSecrets(client: v1.DockerDesktopClient): Promise<Secret[]> {
const response = await client.extension.host?.cli.exec('host-binary', ['list']);
return JSON.parse(response?.stdout || '[]');
}

export async function addSecret(client: v1.DockerDesktopClient, secret: Secret): Promise<void> {
const response = await client.extension.host?.cli.exec('host-binary', ['add', secret.name, secret.value]);
console.log(response);
}

// Get all relevant secrets for a given set of catalog items
export function getAllSecretNames(catalogItems: CatalogItemWithName[]): string[] {
return catalogItems.map((item) => item.secrets || []).flat().map((secret) => secret.name);
}

// Whether or not each secret has been assigned for a given catalog item
export function getAssignedSecrets(catalogItem: CatalogItemWithName, secrets: Secret[]): { name: string, assigned: boolean }[] {
return catalogItem.secrets?.map((secret) => ({ name: secret.name, assigned: secrets.some((s) => s.name === secret.name) })) || [];
}
}

export default Secrets;
19 changes: 17 additions & 2 deletions src/extension/ui/src/components/CatalogGrid.tsx
Original file line number Diff line number Diff line change
@@ -9,6 +9,8 @@ import { getRegistry } from '../Registry';
import { FolderOpenRounded, Search, Settings } from '@mui/icons-material';
import { tryRunImageSync } from '../FileWatcher';
import { CATALOG_URL, POLL_INTERVAL } from '../Constants';
import { SecretList } from './SecretList';
import Secrets from '../Secrets';

interface CatalogGridProps {
registryItems: { [key: string]: { ref: string } };
@@ -38,6 +40,7 @@ export const CatalogGrid: React.FC<CatalogGridProps> = ({
const [showReloadModal, setShowReloadModal] = useState<boolean>(false);
const [search, setSearch] = useState<string>('');
const [tab, setTab] = useState<number>(0);
const [secrets, setSecrets] = useState<Secrets.Secret[]>([]);

const filteredCatalogItems = filterCatalog(catalogItems, registryItems, showRegistered, showUnregistered, search);

@@ -65,6 +68,12 @@ export const CatalogGrid: React.FC<CatalogGridProps> = ({
}
}

const loadSecrets = async () => {
const response = await Secrets.getSecrets(client);
console.log(response);
setSecrets(response);
}

const registerCatalogItem = async (item: CatalogItemWithName) => {
try {
const currentRegistry = await getRegistry(client);
@@ -107,8 +116,10 @@ export const CatalogGrid: React.FC<CatalogGridProps> = ({

useEffect(() => {
loadCatalog(false);
loadSecrets();
const interval = setInterval(() => {
loadCatalog(false);
loadSecrets();
}, POLL_INTERVAL);
return () => {
clearInterval(interval);
@@ -144,10 +155,13 @@ export const CatalogGrid: React.FC<CatalogGridProps> = ({
</Alert>}
<Tabs value={tab} onChange={(e, v) => setTab(v)} sx={{ mb: 0, mt: 1 }}>
<Tooltip title="These are all of the tiles you have available across the catalog.">
<Tab sx={{ fontSize: '1.5em' }} label="Available" />
<Tab sx={{ fontSize: '1.5em' }} label="Tool Catalog" />
</Tooltip>
<Tooltip title="These are tiles which you have allowed MCP clients to use.">
<Tab sx={{ fontSize: '1.5em' }} label="Allowed" />
<Tab sx={{ fontSize: '1.5em' }} label="Your Tools" />
</Tooltip>
<Tooltip title="These are secrets which you have set for your MCP clients.">
<Tab sx={{ fontSize: '1.5em' }} label="Secrets" />
</Tooltip>
</Tabs>
<FormGroup sx={{ width: '100%', mt: 0 }}>
@@ -203,6 +217,7 @@ export const CatalogGrid: React.FC<CatalogGridProps> = ({
</Grid2>
))}
</Grid2>}
{tab === 2 && <SecretList secrets={secrets} />}
</Stack >
);
};
Loading
Oops, something went wrong.

0 comments on commit d9f40aa

Please sign in to comment.