-
Notifications
You must be signed in to change notification settings - Fork 222
Expand file tree
/
Copy pathtools_deploy.go
More file actions
143 lines (132 loc) · 6.22 KB
/
Copy pathtools_deploy.go
File metadata and controls
143 lines (132 loc) · 6.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package mcp
import (
"bufio"
"bytes"
"context"
"fmt"
"strings"
"github.com/modelcontextprotocol/go-sdk/mcp"
fn "knative.dev/func/pkg/functions"
)
var deployTool = &mcp.Tool{
Name: "deploy",
Title: "Deploy Function",
Description: "Deploy a Function. Builds the container as needed.",
Annotations: &mcp.ToolAnnotations{
Title: "Deploy Function",
ReadOnlyHint: false,
DestructiveHint: ptr(false),
IdempotentHint: true, // Deploying the same function configuration multiple times converges to the same desired state.
},
}
func (s *Server) deployHandler(ctx context.Context, r *mcp.CallToolRequest, input DeployInput) (result *mcp.CallToolResult, output DeployOutput, err error) {
if s.readonly {
err = fmt.Errorf("the server is currently in readonly mode. Please set FUNC_ENABLE_MCP_WRITE and restart the client")
return
}
out, err := s.executor.Execute(ctx, "deploy", input.Args()...)
if err != nil {
err = fmt.Errorf("%w\n%s", err, string(out))
return
}
url, urlErr := parseDeployedURL(out)
output = DeployOutput{
Message: string(out),
URL: url,
}
if urlErr != nil {
output.Message += fmt.Sprintf("\n(warning: could not parse deployed URL from output: %v)", urlErr)
}
if f, ferr := fn.NewFunction(input.Path); ferr == nil {
output.Image = f.Deploy.Image
} else {
output.Message += fmt.Sprintf("\n(warning: could not read deployed image from func.yaml: %v)", ferr)
}
return
}
// parseDeployedURL extracts the deployed function URL from combined command
// output. It handles two formats produced by the func CLI:
//
// - Local deploy (written to stderr by the functions client):
// "✅ Function deployed/updated in namespace "ns" and exposed at URL: \n <url>"
//
// - Remote pipeline deploy (written to stdout by cmd/deploy.go):
// "Function Deployed at <url>"
func parseDeployedURL(out []byte) (string, error) {
scanner := bufio.NewScanner(bytes.NewReader(out))
scanner.Buffer(make([]byte, 1024*1024), 1024*1024) // handle large/verbose deploy output
urlNext := false
for scanner.Scan() {
line := scanner.Text()
if urlNext {
if u := strings.TrimSpace(line); u != "" {
return u, nil
}
}
// Local deploy: URL follows on the next non-empty line after this marker.
if strings.Contains(line, "exposed at URL:") {
urlNext = true
continue
}
// Remote pipeline deploy: URL is on the same line after the prefix.
const remotePrefix = "Function Deployed at "
if idx := strings.Index(line, remotePrefix); idx >= 0 {
if u := strings.TrimSpace(line[idx+len(remotePrefix):]); u != "" {
return u, nil
}
}
}
return "", scanner.Err()
}
// DeployInput defines the input parameters for the deploy tool.
type DeployInput struct {
Path string `json:"path" jsonschema:"required,Path to the function project directory"`
Builder *string `json:"builder,omitempty" jsonschema:"Builder to use (pack, s2i, or host)"`
Registry *string `json:"registry,omitempty" jsonschema:"Container registry for function image"`
Image *string `json:"image,omitempty" jsonschema:"Full image name (overrides registry)"`
Namespace *string `json:"namespace,omitempty" jsonschema:"Kubernetes namespace to deploy into"`
GitURL *string `json:"gitUrl,omitempty" jsonschema:"Git URL containing the function source"`
GitBranch *string `json:"gitBranch,omitempty" jsonschema:"Git branch for remote deployment"`
GitDir *string `json:"gitDir,omitempty" jsonschema:"Directory inside the Git repository"`
BuilderImage *string `json:"builderImage,omitempty" jsonschema:"Custom builder image"`
Domain *string `json:"domain,omitempty" jsonschema:"Domain for the function route"`
Platform *string `json:"platform,omitempty" jsonschema:"Target platform (e.g., linux/amd64)"`
Build *string `json:"build,omitempty" jsonschema:"Build control: true, false, or auto"`
PVCSize *string `json:"pvcSize,omitempty" jsonschema:"Custom volume size for remote builds"`
ServiceAccount *string `json:"serviceAccount,omitempty" jsonschema:"Kubernetes ServiceAccount to use"`
RemoteStorageClass *string `json:"remoteStorageClass,omitempty" jsonschema:"Storage class for remote volume"`
Push *bool `json:"push,omitempty" jsonschema:"Push image to registry before deployment"`
RegistryInsecure *bool `json:"registryInsecure,omitempty" jsonschema:"Skip TLS verification for registry"`
BuildTimestamp *bool `json:"buildTimestamp,omitempty" jsonschema:"Use actual time in image metadata"`
Remote *bool `json:"remote,omitempty" jsonschema:"Trigger remote deployment"`
Verbose *bool `json:"verbose,omitempty" jsonschema:"Enable verbose logging output"`
}
func (i DeployInput) Args() []string {
args := []string{"--path", i.Path}
args = appendStringFlag(args, "--builder", i.Builder)
args = appendStringFlag(args, "--registry", i.Registry)
args = appendStringFlag(args, "--image", i.Image)
args = appendStringFlag(args, "--namespace", i.Namespace)
args = appendStringFlag(args, "--git-url", i.GitURL)
args = appendStringFlag(args, "--git-branch", i.GitBranch)
args = appendStringFlag(args, "--git-dir", i.GitDir)
args = appendStringFlag(args, "--builder-image", i.BuilderImage)
args = appendStringFlag(args, "--domain", i.Domain)
args = appendStringFlag(args, "--platform", i.Platform)
args = appendStringFlag(args, "--build", i.Build)
args = appendStringFlag(args, "--pvc-size", i.PVCSize)
args = appendStringFlag(args, "--service-account", i.ServiceAccount)
args = appendStringFlag(args, "--remote-storage-class", i.RemoteStorageClass)
args = appendBoolFlag(args, "--push", i.Push)
args = appendBoolFlag(args, "--registry-insecure", i.RegistryInsecure)
args = appendBoolFlag(args, "--build-timestamp", i.BuildTimestamp)
args = appendBoolFlag(args, "--remote", i.Remote)
args = appendBoolFlag(args, "--verbose", i.Verbose)
return args
}
// DeployOutput defines the structured output returned by the deploy tool.
type DeployOutput struct {
URL string `json:"url,omitempty" jsonschema:"The deployed Function URL"`
Image string `json:"image,omitempty" jsonschema:"The Function image name"`
Message string `json:"message" jsonschema:"Output message"`
}