Skip to content

Commit d7f03c0

Browse files
authored
Merge pull request #142 from mqasimsarfraz/chore/ig-upgrade
chore/inspektor_gadget: Add upgrade action
2 parents 6d4d5fe + 801d355 commit d7f03c0

File tree

11 files changed

+227
-83
lines changed

11 files changed

+227
-83
lines changed

docs/inspektor-gadget-usage.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ Can you observe system calls for the pod my-pod in the default namespace for few
6767

6868
## Prerequisites
6969

70-
- A kubeconfig file that has access to the AKS cluster. You will need to restart the MCP server if you change the kubeconfig file.
70+
- A kubeconfig file that has access to the AKS cluster.
7171
- The tool requires Inspektor Gadget to be installed in the cluster. If you are running with `--access-level=readwrite` or more, the MCP server will automatically
7272
install Inspektor Gadget (action `deploy` ) in the cluster otherwise you can follow the steps to install it manually: [Inspektor Gadget Installation](https://learn.microsoft.com/en-us/troubleshoot/azure/azure-kubernetes/logs/capture-system-insights-from-aks#how-to-install-inspektor-gadget-in-an-aks-cluster) or
7373
use the official Helm chart: [Inspektor Gadget Helm Chart](https://inspektor-gadget.io/docs/latest/reference/install-kubernetes#installation-with-the-helm-chart):

internal/components/inspektorgadget/const.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ const (
66
deployAction = "deploy"
77
// undeployAction is the action to remove Inspektor Gadget from the cluster
88
undeployAction = "undeploy"
9+
// upgradeAction is the action to upgrade Inspektor Gadget in the cluster
10+
upgradeAction = "upgrade"
911
// isDeployedAction is the action to check if Inspektor Gadget is deployed
1012
isDeployedAction = "is_deployed"
1113
)

internal/components/inspektorgadget/gadgetmanager.go

Lines changed: 69 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"fmt"
88
"slices"
99
"strings"
10+
"sync"
1011
"time"
1112

1213
"github.com/inspektor-gadget/inspektor-gadget/pkg/datasource"
@@ -15,7 +16,6 @@ import (
1516
gadgetcontext "github.com/inspektor-gadget/inspektor-gadget/pkg/gadget-context"
1617
"github.com/inspektor-gadget/inspektor-gadget/pkg/operators"
1718
"github.com/inspektor-gadget/inspektor-gadget/pkg/operators/simple"
18-
"github.com/inspektor-gadget/inspektor-gadget/pkg/runtime"
1919
grpcruntime "github.com/inspektor-gadget/inspektor-gadget/pkg/runtime/grpc"
2020
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2121
"k8s.io/cli-runtime/pkg/genericclioptions"
@@ -40,8 +40,8 @@ type GadgetManager interface {
4040
ListGadgets(ctx context.Context) ([]*GadgetInstance, error)
4141
// IsDeployed checks if the Inspektor Gadget is deployed in the environment
4242
IsDeployed(ctx context.Context) (bool, string, error)
43-
// Close closes the gadget manager and releases any resources
44-
Close() error
43+
// GetVersion retrieves the version of Inspektor Gadget installed in the cluster
44+
GetVersion() (string, error)
4545
}
4646

4747
// GadgetInstance represents a running gadget instance
@@ -60,25 +60,12 @@ func init() {
6060
}
6161

6262
// NewGadgetManager creates a new instance of GadgetManager
63-
func NewGadgetManager() (GadgetManager, error) {
64-
rt := grpcruntime.New(grpcruntime.WithConnectUsingK8SProxy)
65-
if err := rt.Init(nil); err != nil {
66-
return nil, fmt.Errorf("initializing gadget runtime: %w", err)
67-
}
68-
69-
restConfig, err := KubernetesFlags.ToRESTConfig()
70-
if err != nil {
71-
return nil, fmt.Errorf("creating REST config: %w", err)
72-
}
73-
rt.SetRestConfig(restConfig)
74-
75-
return &manager{
76-
runtime: rt,
77-
}, nil
63+
func NewGadgetManager() GadgetManager {
64+
return &manager{}
7865
}
7966

8067
type manager struct {
81-
runtime runtime.Runtime
68+
formatterMu sync.Mutex
8269
}
8370

8471
// RunGadget runs a gadget with the specified image and parameters for a given duration
@@ -88,15 +75,20 @@ func (g *manager) RunGadget(ctx context.Context, image string, params map[string
8875
ctx,
8976
image,
9077
gadgetcontext.WithDataOperators(
91-
outputDataOperator(func(data []byte) {
78+
g.outputDataOperator(func(data []byte) {
9279
results.Write(data)
9380
results.WriteByte('\n')
9481
}),
9582
),
9683
gadgetcontext.WithTimeout(duration),
9784
)
9885

99-
if err := g.runtime.RunGadget(gadgetCtx, g.runtime.ParamDescs().ToParams(), params); err != nil {
86+
rt, err := getRuntime()
87+
if err != nil {
88+
return "", fmt.Errorf("getting runtime: %w", err)
89+
}
90+
91+
if err := rt.RunGadget(gadgetCtx, rt.ParamDescs().ToParams(), params); err != nil {
10092
return "", fmt.Errorf("running gadget: %w", err)
10193
}
10294

@@ -118,7 +110,7 @@ func truncateResults(results string, latest bool) string {
118110
return fmt.Sprintf("\n<isTruncated>true</isTruncated>\n<results>%s</results>\n", truncated)
119111
}
120112

121-
func outputDataOperator(cb func(data []byte)) operators.DataOperator {
113+
func (g *manager) outputDataOperator(cb func(data []byte)) operators.DataOperator {
122114
const opPriority = 50000
123115
return simple.New("outputDataOperator",
124116
simple.OnInit(func(gadgetCtx operators.GadgetContext) error {
@@ -135,6 +127,8 @@ func outputDataOperator(cb func(data []byte)) operators.DataOperator {
135127
}
136128

137129
err := d.Subscribe(func(source datasource.DataSource, data datasource.Data) error {
130+
g.formatterMu.Lock()
131+
defer g.formatterMu.Unlock()
138132
jsonData := jsonFormatter.Marshal(data)
139133
cb(jsonData)
140134
return nil
@@ -155,10 +149,14 @@ func (g *manager) StartGadget(ctx context.Context, image string, params map[stri
155149
image,
156150
)
157151

158-
p := g.runtime.ParamDescs().ToParams()
152+
rt, err := getRuntime()
153+
if err != nil {
154+
return "", fmt.Errorf("getting runtime: %w", err)
155+
}
156+
p := rt.ParamDescs().ToParams()
159157

160158
newID := make([]byte, 16)
161-
_, err := rand.Read(newID)
159+
_, err = rand.Read(newID)
162160
if err != nil {
163161
return "", fmt.Errorf("generating new gadget ID: %w", err)
164162
}
@@ -175,7 +173,7 @@ func (g *manager) StartGadget(ctx context.Context, image string, params map[stri
175173
if err = p.Set(grpcruntime.ParamTags, strings.Join(append(tags, "createdBy=aks-mcp"), ",")); err != nil {
176174
return "", fmt.Errorf("setting gadget tags: %w", err)
177175
}
178-
if err := g.runtime.RunGadget(gadgetCtx, p, params); err != nil {
176+
if err = rt.RunGadget(gadgetCtx, p, params); err != nil {
179177
return "", fmt.Errorf("running gadget: %w", err)
180178
}
181179

@@ -184,8 +182,13 @@ func (g *manager) StartGadget(ctx context.Context, image string, params map[stri
184182

185183
// StopGadget stops a running gadget by its ID
186184
func (g *manager) StopGadget(ctx context.Context, id string) error {
187-
if err := g.runtime.(*grpcruntime.Runtime).RemoveGadgetInstance(ctx, g.runtime.ParamDescs().ToParams(), id); err != nil {
188-
return fmt.Errorf("stopping to gadget: %w", err)
185+
rt, err := getRuntime()
186+
if err != nil {
187+
return fmt.Errorf("getting runtime: %w", err)
188+
}
189+
190+
if err = rt.RemoveGadgetInstance(ctx, rt.ParamDescs().ToParams(), id); err != nil {
191+
return fmt.Errorf("stopping gadget: %w", err)
189192
}
190193
return nil
191194
}
@@ -200,7 +203,7 @@ func (g *manager) GetResults(ctx context.Context, id string) (string, error) {
200203
to,
201204
id,
202205
gadgetcontext.WithDataOperators(
203-
outputDataOperator(func(data []byte) {
206+
g.outputDataOperator(func(data []byte) {
204207
results.Write(data)
205208
results.WriteByte('\n')
206209
}),
@@ -210,7 +213,12 @@ func (g *manager) GetResults(ctx context.Context, id string) (string, error) {
210213
gadgetcontext.WithTimeout(time.Second),
211214
)
212215

213-
if err := g.runtime.RunGadget(gadgetCtx, g.runtime.ParamDescs().ToParams(), map[string]string{}); err != nil {
216+
rt, err := getRuntime()
217+
if err != nil {
218+
return "", fmt.Errorf("getting runtime: %w", err)
219+
}
220+
221+
if err = rt.RunGadget(gadgetCtx, rt.ParamDescs().ToParams(), map[string]string{}); err != nil {
214222
return "", fmt.Errorf("attaching to gadget: %w", err)
215223
}
216224

@@ -219,7 +227,12 @@ func (g *manager) GetResults(ctx context.Context, id string) (string, error) {
219227

220228
// ListGadgets lists all running gadgets and returns their instances
221229
func (g *manager) ListGadgets(ctx context.Context) ([]*GadgetInstance, error) {
222-
instances, err := g.runtime.(*grpcruntime.Runtime).GetGadgetInstances(ctx, g.runtime.ParamDescs().ToParams())
230+
rt, err := getRuntime()
231+
if err != nil {
232+
return nil, fmt.Errorf("getting runtime: %w", err)
233+
}
234+
235+
instances, err := rt.GetGadgetInstances(ctx, rt.ParamDescs().ToParams())
223236
if err != nil {
224237
return nil, fmt.Errorf("listing gadgets: %w", err)
225238
}
@@ -267,10 +280,31 @@ func (g *manager) IsDeployed(ctx context.Context) (bool, string, error) {
267280
return true, namespaces[0], nil
268281
}
269282

270-
// Close closes the gadget manager and releases any resources
271-
func (g *manager) Close() error {
272-
if g.runtime != nil {
273-
return g.runtime.Close()
283+
func (g *manager) GetVersion() (string, error) {
284+
rt, err := getRuntime()
285+
if err != nil {
286+
return "", fmt.Errorf("getting runtime: %w", err)
274287
}
275-
return nil
288+
289+
info, err := rt.GetInfo()
290+
if err != nil {
291+
return "", fmt.Errorf("getting info: %w", err)
292+
}
293+
return info.ServerVersion, nil
294+
}
295+
296+
// getRuntime sets up a runtime, ensuring we always use the latest kubeconfig
297+
func getRuntime() (*grpcruntime.Runtime, error) {
298+
rt := grpcruntime.New(grpcruntime.WithConnectUsingK8SProxy)
299+
if err := rt.Init(nil); err != nil {
300+
return nil, fmt.Errorf("initializing gadget runtime: %w", err)
301+
}
302+
303+
restConfig, err := KubernetesFlags.ToRESTConfig()
304+
if err != nil {
305+
return nil, fmt.Errorf("creating REST config: %w", err)
306+
}
307+
rt.SetRestConfig(restConfig)
308+
309+
return rt, nil
276310
}

internal/components/inspektorgadget/gadgets.go

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,14 @@ type Gadget struct {
1919
ParamsFunc func(filterParams map[string]interface{}, gadgetParams map[string]string)
2020
}
2121

22+
func (g *Gadget) getImage(version string) string {
23+
return fmt.Sprintf("%s:%s", g.Image, gadgetVersionFor(version))
24+
}
25+
2226
var gadgets = []Gadget{
2327
{
2428
Name: observeDNS,
25-
Image: "ghcr.io/inspektor-gadget/gadget/trace_dns:latest",
29+
Image: "ghcr.io/inspektor-gadget/gadget/trace_dns",
2630
Description: "Observes DNS queries in the cluster",
2731
Params: map[string]interface{}{
2832
"name": map[string]interface{}{
@@ -75,7 +79,7 @@ var gadgets = []Gadget{
7579
},
7680
{
7781
Name: observeTCP,
78-
Image: "ghcr.io/inspektor-gadget/gadget/trace_tcp:latest",
82+
Image: "ghcr.io/inspektor-gadget/gadget/trace_tcp",
7983
Description: "Observes TCP traffic in the cluster",
8084
Params: map[string]interface{}{
8185
"source_port": map[string]interface{}{
@@ -121,7 +125,7 @@ var gadgets = []Gadget{
121125
},
122126
{
123127
Name: observeFileOpen,
124-
Image: "ghcr.io/inspektor-gadget/gadget/trace_open:latest",
128+
Image: "ghcr.io/inspektor-gadget/gadget/trace_open",
125129
Description: "Observes file open operations in the cluster",
126130
Params: map[string]interface{}{
127131
"path": map[string]interface{}{
@@ -152,7 +156,7 @@ var gadgets = []Gadget{
152156
},
153157
{
154158
Name: observeProcessExecution,
155-
Image: "ghcr.io/inspektor-gadget/gadget/trace_exec:latest",
159+
Image: "ghcr.io/inspektor-gadget/gadget/trace_exec",
156160
Description: "Observes process execution in the cluster",
157161
Params: map[string]interface{}{
158162
"command": map[string]interface{}{
@@ -172,7 +176,7 @@ var gadgets = []Gadget{
172176
},
173177
{
174178
Name: observeSignal,
175-
Image: "ghcr.io/inspektor-gadget/gadget/trace_signal:latest",
179+
Image: "ghcr.io/inspektor-gadget/gadget/trace_signal",
176180
Description: "Traces signals sent to containers in the cluster",
177181
Params: map[string]interface{}{
178182
"signal": map[string]interface{}{
@@ -193,7 +197,7 @@ var gadgets = []Gadget{
193197
},
194198
{
195199
Name: observeSystemCalls,
196-
Image: "ghcr.io/inspektor-gadget/gadget/traceloop:latest",
200+
Image: "ghcr.io/inspektor-gadget/gadget/traceloop",
197201
Description: "Observes system calls in the cluster",
198202
Params: map[string]interface{}{
199203
"syscall": map[string]interface{}{
@@ -215,7 +219,7 @@ var gadgets = []Gadget{
215219
},
216220
{
217221
Name: topFile,
218-
Image: "ghcr.io/inspektor-gadget/gadget/top_file:latest",
222+
Image: "ghcr.io/inspektor-gadget/gadget/top_file",
219223
Description: "Shows top files by read/write operations",
220224
Params: map[string]interface{}{
221225
"max_entries": map[string]interface{}{
@@ -240,7 +244,7 @@ var gadgets = []Gadget{
240244
},
241245
{
242246
Name: topTCP,
243-
Image: "ghcr.io/inspektor-gadget/gadget/top_tcp:latest",
247+
Image: "ghcr.io/inspektor-gadget/gadget/top_tcp",
244248
Description: "Shows top TCP connections by traffic volume",
245249
Params: map[string]interface{}{
246250
"max_entries": map[string]interface{}{

internal/components/inspektorgadget/gadgets_test.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package inspektorgadget
22

3-
import "testing"
3+
import (
4+
"strings"
5+
"testing"
6+
)
47

58
func TestGadgets(t *testing.T) {
69
for _, gadget := range gadgets {
@@ -10,6 +13,9 @@ func TestGadgets(t *testing.T) {
1013
if gadget.Image == "" {
1114
t.Errorf("Gadget image is empty for %s", gadget.Name)
1215
}
16+
if gadget.Image != "" && strings.Contains(gadget.Image, ":") {
17+
t.Errorf("Gadget image %s should not contain a version tag", gadget.Image)
18+
}
1319
if gadget.Description == "" {
1420
t.Errorf("Gadget description is empty for %s", gadget.Name)
1521
}

0 commit comments

Comments
 (0)