github.com/argoproj/argo-cd/v3@v3.2.1/pkg/apiclient/application/forwarder_overwrite.go (about) 1 package application 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 gohttp "net/http" 8 "strings" 9 10 "github.com/argoproj/argo-cd/v3/util/kube" 11 12 "github.com/argoproj/pkg/v2/grpc/http" 13 "github.com/grpc-ecosystem/grpc-gateway/runtime" 14 15 //nolint:staticcheck 16 "github.com/golang/protobuf/proto" 17 18 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1" 19 ) 20 21 // appFields is a map of fields that can be selected from an application. 22 // The manually maintained list is required because application list response might include thousands of applications 23 // and JSON based field handling is too slow. 24 var appFields = map[string]func(app *v1alpha1.Application) any{ 25 "metadata.name": func(app *v1alpha1.Application) any { return app.Name }, 26 "metadata.namespace": func(app *v1alpha1.Application) any { return app.Namespace }, 27 "metadata.annotations": func(app *v1alpha1.Application) any { return app.Annotations }, 28 "metadata.labels": func(app *v1alpha1.Application) any { return app.Labels }, 29 "metadata.creationTimestamp": func(app *v1alpha1.Application) any { return app.CreationTimestamp }, 30 "metadata.deletionTimestamp": func(app *v1alpha1.Application) any { return app.DeletionTimestamp }, 31 "spec": func(app *v1alpha1.Application) any { return app.Spec }, 32 "status.sourceHydrator": func(app *v1alpha1.Application) any { return app.Status.SourceHydrator }, 33 "status.sync.status": func(app *v1alpha1.Application) any { return app.Status.Sync.Status }, 34 "status.health": func(app *v1alpha1.Application) any { return app.Status.Health }, 35 "status.summary": func(app *v1alpha1.Application) any { return app.Status.Summary }, 36 "status.operationState.startedAt": func(app *v1alpha1.Application) any { 37 if app.Status.OperationState != nil { 38 return app.Status.OperationState.StartedAt 39 } 40 return nil 41 }, 42 "status.operationState.finishedAt": func(app *v1alpha1.Application) any { 43 if app.Status.OperationState != nil { 44 return app.Status.OperationState.FinishedAt 45 } 46 return nil 47 }, 48 "status.resources": func(app *v1alpha1.Application) any { 49 if len(app.Status.Resources) > 0 { 50 return app.Status.Resources 51 } 52 return nil 53 }, 54 "operation.sync": func(app *v1alpha1.Application) any { 55 if app.Operation != nil { 56 return app.Operation.Sync 57 } 58 return nil 59 }, 60 "status.operationState.phase": func(app *v1alpha1.Application) any { 61 if app.Status.OperationState != nil { 62 return app.Status.OperationState.Phase 63 } 64 return nil 65 }, 66 "status.operationState.operation.sync": func(app *v1alpha1.Application) any { 67 if app.Status.OperationState != nil { 68 return app.Status.OperationState.SyncResult 69 } 70 return nil 71 }, 72 } 73 74 func processApplicationListField(v any, fields map[string]any, exclude bool) (any, error) { 75 if appList, ok := v.(*v1alpha1.ApplicationList); ok { 76 var items []map[string]any 77 for _, app := range appList.Items { 78 converted := make(map[string]any) 79 items = append(items, converted) 80 for field, fn := range appFields { 81 if _, ok := fields["items."+field]; ok == exclude { 82 continue 83 } 84 value := fn(&app) 85 if value == nil { 86 continue 87 } 88 parts := strings.Split(field, ".") 89 item := converted 90 for i := 0; i < len(parts); i++ { 91 subField := parts[i] 92 if i == len(parts)-1 { 93 item[subField] = value 94 } else { 95 if _, ok := item[subField]; !ok { 96 item[subField] = make(map[string]any) 97 } 98 nestedMap, ok := item[subField].(map[string]any) 99 if !ok { 100 return nil, fmt.Errorf("field %s is not a map", field) 101 } 102 item = nestedMap 103 } 104 } 105 } 106 } 107 return map[string]any{ 108 "items": items, 109 "metadata": appList.ListMeta, 110 }, nil 111 } 112 return nil, errors.New("not an application list") 113 } 114 115 func init() { 116 logsForwarder := func(ctx context.Context, mux *runtime.ServeMux, marshaler runtime.Marshaler, w gohttp.ResponseWriter, req *gohttp.Request, recv func() (proto.Message, error), opts ...func(context.Context, gohttp.ResponseWriter, proto.Message) error) { 117 if req.URL.Query().Get("download") == "true" { 118 w.Header().Set("Content-Type", "application/octet-stream") 119 fileName := "log" 120 namespace := req.URL.Query().Get("namespace") 121 podName := req.URL.Query().Get("podName") 122 container := req.URL.Query().Get("container") 123 if kube.IsValidResourceName(namespace) && kube.IsValidResourceName(podName) && kube.IsValidResourceName(container) { 124 fileName = fmt.Sprintf("%s-%s-%s", namespace, podName, container) 125 } 126 w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment;filename="%s.log"`, fileName)) 127 for { 128 msg, err := recv() 129 if err != nil { 130 _, _ = w.Write([]byte(err.Error())) 131 return 132 } 133 if logEntry, ok := msg.(*LogEntry); ok { 134 if logEntry.GetLast() { 135 return 136 } 137 if _, err = w.Write([]byte(logEntry.GetContent() + "\n")); err != nil { 138 return 139 } 140 } 141 } 142 } else { 143 http.StreamForwarder(ctx, mux, marshaler, w, req, recv, opts...) 144 } 145 } 146 forward_ApplicationService_PodLogs_0 = logsForwarder 147 forward_ApplicationService_PodLogs_1 = logsForwarder 148 forward_ApplicationService_WatchResourceTree_0 = http.StreamForwarder 149 forward_ApplicationService_Watch_0 = http.NewStreamForwarder(func(message proto.Message) (string, error) { 150 event, ok := message.(*v1alpha1.ApplicationWatchEvent) 151 if !ok { 152 return "", errors.New("unexpected message type") 153 } 154 return event.Application.Name, nil 155 }) 156 forward_ApplicationService_List_0 = http.UnaryForwarderWithFieldProcessor(processApplicationListField) 157 forward_ApplicationService_ManagedResources_0 = http.UnaryForwarder 158 }