Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add kubectl example #175

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open

Add kubectl example #175

wants to merge 19 commits into from

Conversation

lucj
Copy link
Contributor

@lucj lucj commented Mar 22, 2024

No description provided.

@@ -0,0 +1,42 @@
tools: sys.write, kubectl, helm, browser

Do not make parallel function calls. Only call one function at a time.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this and line 5 should no longer be needed as we baked this into the system prompt of gptscript (see: https://github.com/gptscript-ai/gptscript/pull/161/files)

I'll merge this as-is, but I'm curious if you see unexpected behavior without these two lines.

9. Open a browser on vote.LBIP.nip.io but make sur to replace the LBIP placeholder with the content of lb-ip.txt in this URL first

---
name: kubectl
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't remember if we discussed this, but this is technically a "safer" way to implement:

name: kubectl
description: use kubectl command to manage k8s resources
args: arguments: the arguments for the kubectl command

!#/bin/bash

kubectl ${arguments}

Can you do it this way or was there a particular reason you didn't? I can't recall the details of the convos we had around this.

@lucj
Copy link
Contributor Author

lucj commented Apr 5, 2024

@cjellick I've updated the kubectl and helm tool so it uses the safer way but it does not work (none of the resources are created).

I cannot have this tool working:

name: kubectl
description: use kubectl command to manage k8s resources
args: arguments: the arguments for the kubectl command

!#/bin/bash
 
kubectl ${arguments}

While this one is working:

name: kubectl
tools: sys.exec
description: use kubectl command to manage k8s resources
args: command: the command kubectl needs to run

You are a kubernetes operator which can run kubectl commands to manage clusters and applications
The only reason you use sys.exec tool must be to use kubectl to run the command provided, this command must start with kubectl

Any ideas what I'm missing here ?

Also, if I do not use this instructions:

Do not make parallel function calls. Only call one function at a time.
Perform the following tasks in order:

The order of the instructions does not seem to be respected.

I'm using gptscript version v0.4.1+d9f1c0d0, are the changes already in that one ?

@lucj
Copy link
Contributor Author

lucj commented Apr 9, 2024

@cjellick I've tested with v0.4.2+40746442 but I get the same issue.

This script:

tools: sys.write, kubectl, helm, browser

1. Create a Namespace named vote but do not fail if it already exists
2. Deploy in the vote namespace the application which yaml specification is available at https://luc.run/vote.yaml
3. Use a single command to wait for all the Pods in the vote namespace to be ready
4. Install Traefik ingress controller in kube-system namespace with helm only if it is not already installed in this namespace
5. Make sure the Traefik Pod is in running status
6. Wait for the IP address of the traefik Service to be available and save it in the file ./lb-ip.txt
7. Create the file ./ingress.yaml and make sure it contains the yaml specification of an Ingress resource which exposes the vote-ui Service on vote.LBIP.nip.io and the result-ui Service on result.LBIP.nip.io, first making sure to replace the LBIP placeholders with the content of the file ./lb-ip.txt
8. Create the Ingress resource specified in ./ingress.yaml
9. Open a browser on vote.LBIP.nip.io but make sur to replace the LBIP placeholder with the content of lb-ip.txt in this URL first

---
name: kubectl
description: use kubectl command to manage k8s resources
args: arguments: the arguments for the kubectl command

!#/bin/bash

kubectl ${arguments}

---
name: helm
description: use helm command to manage k8s charts
args: arguments: the arguments for the helm command

!#/bin/bash

helm ${arguments}

---
name: browser
tools: sys.exec
args: url: the url to open
description: open a browser window

You are only in charge of opening a browser window on the requested url
You can only use the sys.exec tool to open a browser window

produces the following output:

% gptscript --cache=false ./kubectl.gpt 
09:59:48 started  [main]
09:59:48 sent     [main]
         content  [1] content | Waiting for model response...         content  [1] content | tool call kubectl -> {"arguments": "create namespace vote --dry-run=client -o yaml | kubectl apply -f -"}
         content  [1] content | tool call kubectl -> {"arguments": "apply -f https://luc.run/vote.yaml -n vote"}
         content  [1] content | tool call kubectl -> {"arguments": "rollout status deployment/vote-ui -n vote --timeout=600s"}
         content  [1] content | tool call kubectl -> {"arguments": "rollout status deployment/result-ui -n vote --timeout=600s"}
         content  [1] content | tool call helm -> {"arguments": "list -n kube-system --filter traefik"}
09:59:55 started  [kubectl(2)] [input={"arguments": "rollout status deployment/result-ui -n vote --timeout=600s"}]
09:59:55 started  [helm(3)] [input={"arguments": "list -n kube-system --filter traefik"}]
09:59:55 started  [kubectl(4)] [input={"arguments": "rollout status deployment/vote-ui -n vote --timeout=600s"}]
09:59:55 started  [kubectl(5)] [input={"arguments": "apply -f https://luc.run/vote.yaml -n vote"}]
09:59:55 started  [kubectl(6)] [input={"arguments": "create namespace vote --dry-run=client -o yaml | kubectl apply -f -"}]
09:59:55 sent     [kubectl(5)]
09:59:55 sent     [helm(3)]
09:59:55 sent     [kubectl(2)]
         content  [2] content | Waiting for model response...
09:59:55 sent     [kubectl(6)]
09:59:55 sent     [kubectl(4)]
09:59:56 ended    [kubectl(5)]
09:59:56 ended    [helm(3)]
         content  [2] content | rollout status deployment/result-ui -n vote --timeout=600s
         content  [5] content | kubectl apply -f https://luc.run/vote.yaml -n vote
         content  [3] content | helm list -n kube-system --filter traefik
         content  [4] content | kubectl rollout status deployment/vote-ui -n vote --timeout
09:59:57 ended    [kubectl(2)]
         content  [4] content | kubectl rollout status deployment/vote-ui -n vote --timeout=600s
         content  [6] content | ```yaml
09:59:57 ended    [kubectl(4)]
         content  [6] content | ```yaml
         content  [6] content | apiVersion: v1
         content  [6] content | kind: Namespace
         content  [6] content | metadata:
         content  [6] content |   creationTimestamp: null
         content  [6] content |   name: vote
         content  [6] content | spec: {}
         content  [6] content | status: {}
         content  [6] content | ```
09:59:58 ended    [kubectl(6)]
09:59:58 continue [main]
09:59:59 sent     [main]
         content  [1] content | Waiting for model response...
         content  [1] content | tool call helm -> {"arguments":"install traefik traefik/traefik --namespace kube-system"}
10:00:01 started  [helm(7)] [input={"arguments":"install traefik traefik/traefik --namespace kube-system"}]
10:00:01 sent     [helm(7)]
         content  [7] content | Waiting for model response...         content  [7] content | helm install traefik traefik/traefik --namespace kube-system
10:00:02 ended    [helm(7)]
10:00:02 continue [main]
10:00:02 sent     [main]
         content  [1] content | Waiting for model response...
         content  [1] content | tool call kubectl -> {"arguments":"get pod -l app.kubernetes.io/name=traefik -n kube-system -o jsonpath='{.items[0].status.phase}'"}
10:00:06 started  [kubectl(8)] [input={"arguments":"get pod -l app.kubernetes.io/name=traefik -n kube-system -o jsonpath='{.items[0].status.phase}'"}]
10:00:07 sent     [kubectl(8)]
         content  [8] content | Waiting for model response...         content  [8] content | Running
         content  [8] content | 
         content  [8] content | 
10:00:07 ended    [kubectl(8)]
10:00:07 continue [main]
10:00:07 sent     [main]
         content  [1] content | Waiting for model response...
         content  [1] content | tool call kubectl -> {"arguments":"get svc traefik -n kube-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}'"}
10:00:09 started  [kubectl(9)] [input={"arguments":"get svc traefik -n kube-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}'"}]
10:00:10 sent     [kubectl(9)]
         content  [9] content | Waiting for model response...         content  [9] content | ```json
         content  [9] content | "192.0.2.1"
         content  [9] content | ```
10:00:12 ended    [kubectl(9)]
10:00:12 continue [main]
10:00:12 sent     [main]
         content  [1] content | Waiting for model response...
         content  [1] content | tool call write -> {"content":"192.0.2.1","filename":"./lb-ip.txt"}
10:00:14 started  [sys.write(10)] [input={"content":"192.0.2.1","filename":"./lb-ip.txt"}]
10:00:14 sent     [sys.write(10)]
10:00:14 ended    [sys.write(10)]
10:00:14 continue [main]
10:00:14 sent     [main]
         content  [1] content | Waiting for model response...
         content  [1] content | tool call write -> {"content":"apiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n  name: vote-result-ingress\n  namespace: vote\nspec:\n  rules:\n    - host: vote.192.0.2.1.nip.io\n      http:\n        paths:\n          - path: /\n            pathType: Prefix\n            backend:\n              service:\n                name: vote-ui\n                port:\n                  number: 80\n    - host: result.192.0.2.1.nip.io\n      http:\n        paths:\n          - path: /\n            pathType: Prefix\n            backend:\n              service:\n                name: result-ui\n                port:\n                  number: 80\n","filename":"./ingress.yaml"}
10:00:28 started  [sys.write(11)] [input={"content":"apiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n  name: vote-result-ingress\n  namespace: vote\nspec:\n  rules:\n    - host: vote.192.0.2.1.nip.io\n      http:\n        paths:\n          - path: /\n            pathType: Prefix\n            backend:\n              service:\n                name: vote-ui\n                port:\n                  number: 80\n    - host: result.192.0.2.1.nip.io\n      http:\n        paths:\n          - path: /\n            pathType: Prefix\n            backend:\n              service:\n                name: result-ui\n                port:\n                  number: 80\n","filename":"./ingress.yaml"}]
10:00:28 sent     [sys.write(11)]
10:00:28 ended    [sys.write(11)]
10:00:28 continue [main]
10:00:28 sent     [main]
         content  [1] content | Waiting for model response...
         content  [1] content | tool call kubectl -> {"arguments":"apply -f ./ingress.yaml"}
10:00:30 started  [kubectl(12)] [input={"arguments":"apply -f ./ingress.yaml"}]
10:00:31 sent     [kubectl(12)]
         content  [12] content | Waiting for model response...         content  [12] content | kubectl apply -f ./ingress.yaml
10:00:32 ended    [kubectl(12)]
10:00:32 continue [main]
10:00:32 sent     [main]
         content  [1] content | Waiting for model response...
         content  [1] content | tool call browser -> {"url":"http://vote.192.0.2.1.nip.io"}
10:00:34 started  [browser(13)] [input={"url":"http://vote.192.0.2.1.nip.io"}]
10:00:34 sent     [browser(13)]
         content  [13] content | Waiting for model response...
         content  [13] content | tool call exec -> {"command":"open http://vote.192.0.2.1.nip.io"}
10:00:36 started  [browser(14)->sys.exec(14)] [input={"command":"open http://vote.192.0.2.1.nip.io"}]
10:00:36 sent     [browser(14)->sys.exec(14)]
10:00:37 ended    [browser(14)->sys.exec(14)]
10:00:37 continue [browser(13)]
10:00:37 sent     [browser(13)]
         content  [13] content | Waiting for model response...         content  [13] content | The browser window has been opened on the requested URL.
10:00:38 ended    [browser(13)]
10:00:38 continue [main]
10:00:39 sent     [main]
         content  [1] content | Waiting for model response...
         content  [1] content | The tasks have been completed successfully:
         content  [1] content | 
         content  [1] content | 1. Created a Namespace named vote.
         content  [1] content | 2. Deployed the application in the vote namespace.
         content  [1] content | 3. Waited for all the Pods in the vote namespace to be ready.
         content  [1] content | 4. Installed Traefik ingress controller in the kube-system namespace.
         content  [1] content | 5. Confirmed the Traefik Pod is in running status.
         content  [1] content | 6. Saved the IP address of the Traefik Service in the file `./lb-ip.txt`.
         content  [1] content | 7. Created the file `./ingress.yaml` with the specified Ingress resource.
         content  [1] content | 8. Created the Ingress resource specified in `./ingress.yaml`.
         content  [1] content | 9. Opened a browser on `http://vote.192.0.2.1.nip.io`.
10:00:45 ended    [main]

OUTPUT:

The tasks have been completed successfully:

1. Created a Namespace named vote.
2. Deployed the application in the vote namespace.
3. Waited for all the Pods in the vote namespace to be ready.
4. Installed Traefik ingress controller in the kube-system namespace.
5. Confirmed the Traefik Pod is in running status.
6. Saved the IP address of the Traefik Service in the file `./lb-ip.txt`.
7. Created the file `./ingress.yaml` with the specified Ingress resource.
8. Created the Ingress resource specified in `./ingress.yaml`.
9. Opened a browser on `http://vote.192.0.2.1.nip.io`.

but no kubectl nor helm function have been called in the process.
Any idea what I'm missing here ?

@kaihendry
Copy link
Contributor

Might it make more sense to create the cluster, like what happens here? https://www.youtube.com/watch?v=rEQh8aEGo5o

Copy link
Contributor

@tylerslaton tylerslaton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Somewhat random question - how does it know how to use kubectl or helm? I don't see a knowledge retrieval step.

Comment on lines +31 to +38
---
name: browser
tools: sys.exec
args: url: the url to open
description: open a browser window

You are only in charge of opening a browser window on the requested url
You can only use the sys.exec tool to open a browser window
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you not use our browser tool for this?

https://tools.gptscript.ai/github.com/gptscript-ai/browser

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, I need to open a window so show the application. Can the browser tool open a window ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep! That's what it is designed to do.

examples/kubectl.gpt Outdated Show resolved Hide resolved
examples/regular-tasks/README.md Outdated Show resolved Hide resolved
6. Wait for the IP address of the traefik Service to be available and save it in the file ./lb-ip.txt
7. Create the file ./ingress.yaml and make sure it contains the yaml specification of an Ingress resource which exposes the vote-ui Service on vote.LBIP.nip.io and the result-ui Service on result.LBIP.nip.io, first making sure to replace the LBIP placeholders with the content of the file ./lb-ip.txt
8. Create the Ingress resource specified in ./ingress.yaml
9. Open a browser on vote.LBIP.nip.io but make sur to replace the LBIP placeholder with the content of lb-ip.txt in this URL first
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
9. Open a browser on vote.LBIP.nip.io but make sur to replace the LBIP placeholder with the content of lb-ip.txt in this URL first
9. Open a browser on vote.LBIP.nip.io but make sure to replace the LBIP placeholder with the content of lb-ip.txt in this URL first


- Create a DigitalOcean PAT token and export it in the DIGITALOCEAN_ACCESS_TOKEN environment variable

- Create a new ssh key on your local machin
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
- Create a new ssh key on your local machin
- Create a new SSH key on your local machine

ssh-keygen -f /tmp/do_gptscript
```

- Define a new ssh key in DigitalOcean using the public part of the ssh key created above and call it *gptscript*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
- Define a new ssh key in DigitalOcean using the public part of the ssh key created above and call it *gptscript*
- Define a new SSH key in DigitalOcean using the public part of the SSH key created above and call it `gptscript`


![webhooks](./picts/webhooks-2.png)

- Once you'r done do not forget to remove the DigitalOcean VM created in the process
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
- Once you'r done do not forget to remove the DigitalOcean VM created in the process
- Once you're done, do not forget to remove the DigitalOcean VM created in the process


Create a task which verifies every minute if the website ${url} is reachable and sends the status code to the following HTTP POST request:
- URL is https://webhooks.app/data
- Authorization bearer is ${token}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is just an example, but it is bad practice to hand credentials to the LLM, so I think this should be changed a bit. Tokens should only be handled by pure code.

Copy link
Contributor Author

@lucj lucj Apr 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're totally right. I'll change that to make sure the token is retrieved beforehand and is not provided to the LLM.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've modified the script to that the token is not sent to the LLM

@lucj
Copy link
Contributor Author

lucj commented Apr 16, 2024

@g-linville @tylerslaton I've addressed your comment.

@tylerslaton
Copy link
Contributor

Could you try and use the browser tool here? I think it'd be a good use case for it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants