1 package apiserver_impl
2
3 import (
4 "context"
5 "fmt"
6 "net/http"
7 "os"
8 "path/filepath"
9
10 openapi "github.com/redhat-developer/odo/pkg/apiserver-gen/go"
11 "github.com/redhat-developer/odo/pkg/apiserver-impl/devstate"
12 "github.com/redhat-developer/odo/pkg/component/describe"
13 "github.com/redhat-developer/odo/pkg/devfile"
14 "github.com/redhat-developer/odo/pkg/devfile/validate"
15 "github.com/redhat-developer/odo/pkg/kclient"
16 fcontext "github.com/redhat-developer/odo/pkg/odo/commonflags/context"
17 odocontext "github.com/redhat-developer/odo/pkg/odo/context"
18 "github.com/redhat-developer/odo/pkg/podman"
19 "github.com/redhat-developer/odo/pkg/preference"
20 "github.com/redhat-developer/odo/pkg/segment"
21 scontext "github.com/redhat-developer/odo/pkg/segment/context"
22 "github.com/redhat-developer/odo/pkg/state"
23 "k8s.io/klog"
24 )
25
26
27
28
29 type DefaultApiService struct {
30 cancel context.CancelFunc
31 pushWatcher chan<- struct{}
32 kubeClient kclient.ClientInterface
33 podmanClient podman.Client
34 stateClient state.Client
35 preferenceClient preference.Client
36
37 devfileState devstate.DevfileState
38 }
39
40
41 func NewDefaultApiService(
42 cancel context.CancelFunc,
43 pushWatcher chan<- struct{},
44 kubeClient kclient.ClientInterface,
45 podmanClient podman.Client,
46 stateClient state.Client,
47 preferenceClient preference.Client,
48 ) openapi.DefaultApiServicer {
49 return &DefaultApiService{
50 cancel: cancel,
51 pushWatcher: pushWatcher,
52 kubeClient: kubeClient,
53 podmanClient: podmanClient,
54 stateClient: stateClient,
55 preferenceClient: preferenceClient,
56
57 devfileState: devstate.NewDevfileState(),
58 }
59 }
60
61
62 func (s *DefaultApiService) ComponentCommandPost(ctx context.Context, componentCommandPostRequest openapi.ComponentCommandPostRequest) (openapi.ImplResponse, error) {
63 switch componentCommandPostRequest.Name {
64 case "push":
65 select {
66 case s.pushWatcher <- struct{}{}:
67 return openapi.Response(http.StatusOK, openapi.GeneralSuccess{
68 Message: "push was successfully executed",
69 }), nil
70 default:
71 return openapi.Response(http.StatusTooManyRequests, openapi.GeneralError{
72 Message: "a push operation is not possible at this time. Please retry later",
73 }), nil
74 }
75
76 default:
77 return openapi.Response(http.StatusBadRequest, openapi.GeneralError{
78 Message: fmt.Sprintf("command name %q not supported. Supported values are: %q", componentCommandPostRequest.Name, "push"),
79 }), nil
80 }
81 }
82
83
84 func (s *DefaultApiService) ComponentGet(ctx context.Context) (openapi.ImplResponse, error) {
85 value, _, err := describe.DescribeDevfileComponent(ctx, s.kubeClient, s.podmanClient, s.stateClient)
86 if err != nil {
87 return openapi.Response(http.StatusInternalServerError, openapi.GeneralError{
88 Message: fmt.Sprintf("error getting the description of the component: %s", err),
89 }), nil
90 }
91 return openapi.Response(http.StatusOK, value), nil
92 }
93
94
95 func (s *DefaultApiService) InstanceDelete(ctx context.Context) (openapi.ImplResponse, error) {
96 s.cancel()
97 return openapi.Response(http.StatusOK, openapi.GeneralSuccess{
98 Message: fmt.Sprintf("'odo dev' instance with pid: %d is shutting down.", odocontext.GetPID(ctx)),
99 }), nil
100 }
101
102
103 func (s *DefaultApiService) InstanceGet(ctx context.Context) (openapi.ImplResponse, error) {
104 response := openapi.InstanceGet200Response{
105 Pid: int32(odocontext.GetPID(ctx)),
106 ComponentDirectory: odocontext.GetWorkingDirectory(ctx),
107 }
108 return openapi.Response(http.StatusOK, response), nil
109 }
110
111 func (s *DefaultApiService) DevfileGet(ctx context.Context) (openapi.ImplResponse, error) {
112 devfilePath := odocontext.GetDevfilePath(ctx)
113 content, err := os.ReadFile(devfilePath)
114 if err != nil {
115 return openapi.Response(http.StatusInternalServerError, openapi.GeneralError{
116 Message: fmt.Sprintf("error getting Devfile content: %s", err),
117 }), nil
118 }
119 return openapi.Response(http.StatusOK, openapi.DevfileGet200Response{
120 Content: string(content),
121 }), nil
122
123 }
124
125 func (s *DefaultApiService) DevfilePut(ctx context.Context, params openapi.DevfilePutRequest) (openapi.ImplResponse, error) {
126
127 tmpdir, err := func() (string, error) {
128 dir, err := os.MkdirTemp("", "odo")
129 if err != nil {
130 return "", err
131 }
132 return dir, os.WriteFile(filepath.Join(dir, "devfile.yaml"), []byte(params.Content), 0600)
133 }()
134 defer func() {
135 if tmpdir != "" {
136 err = os.RemoveAll(tmpdir)
137 if err != nil {
138 klog.V(1).Infof("Error deleting temp directory %q: %s", tmpdir, err)
139 }
140 }
141 }()
142 if err != nil {
143 return openapi.Response(http.StatusInternalServerError, openapi.GeneralError{
144 Message: fmt.Sprintf("error saving temp Devfile: %s", err),
145 }), nil
146 }
147
148 err = s.validateDevfile(ctx, tmpdir)
149 if err != nil {
150 return openapi.Response(http.StatusInternalServerError, openapi.GeneralError{
151 Message: fmt.Sprintf("error validating Devfile: %s", err),
152 }), nil
153 }
154
155 devfilePath := odocontext.GetDevfilePath(ctx)
156 err = os.WriteFile(devfilePath, []byte(params.Content), 0600)
157 if err != nil {
158 return openapi.Response(http.StatusInternalServerError, openapi.GeneralError{
159 Message: fmt.Sprintf("error writing Devfile content to %q: %s", devfilePath, err),
160 }), nil
161 }
162
163 return openapi.Response(http.StatusOK, openapi.GeneralSuccess{
164 Message: "devfile has been successfully written to disk",
165 }), nil
166
167 }
168
169 func (s *DefaultApiService) validateDevfile(ctx context.Context, dir string) error {
170 var (
171 variables = fcontext.GetVariables(ctx)
172 imageRegistry = s.preferenceClient.GetImageRegistry()
173 )
174 devObj, err := devfile.ParseAndValidateFromFileWithVariables(dir, variables, imageRegistry, false)
175 if err != nil {
176 return fmt.Errorf("failed to parse the devfile: %w", err)
177 }
178 return validate.ValidateDevfileData(devObj.Data)
179 }
180
181 func (s *DefaultApiService) TelemetryGet(ctx context.Context) (openapi.ImplResponse, error) {
182 var (
183 enabled = scontext.GetTelemetryStatus(ctx)
184 apikey string
185 userid string
186 )
187 if enabled {
188 apikey = segment.GetApikey()
189 var err error
190 userid, err = segment.GetUserIdentity(segment.GetTelemetryFilePath())
191 if err != nil {
192 return openapi.Response(http.StatusInternalServerError, openapi.GeneralError{
193 Message: fmt.Sprintf("error getting telemetry data: %s", err),
194 }), nil
195 }
196 }
197
198 return openapi.Response(http.StatusOK, openapi.TelemetryResponse{
199 Enabled: enabled,
200 Apikey: apikey,
201 Userid: userid,
202 }), nil
203 }
204
View as plain text