• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / mender-server / 1495380963

14 Oct 2024 03:35PM UTC coverage: 70.373% (-2.5%) from 72.904%
1495380963

Pull #101

gitlab-ci

mineralsfree
feat: tenant list added

Ticket: MEN-7568
Changelog: None

Signed-off-by: Mikita Pilinka <mikita.pilinka@northern.tech>
Pull Request #101: feat: tenant list added

4406 of 6391 branches covered (68.94%)

Branch coverage included in aggregate %.

88 of 183 new or added lines in 10 files covered. (48.09%)

2623 existing lines in 65 files now uncovered.

36673 of 51982 relevant lines covered (70.55%)

31.07 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/backend/services/create-artifact-worker/cmd/single-file.go
1
// Copyright 2023 Northern.tech AS
2
//
3
//        Licensed under the Apache License, Version 2.0 (the "License");
4
//        you may not use this file except in compliance with the License.
5
//        You may obtain a copy of the License at
6
//
7
//            http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//        Unless required by applicable law or agreed to in writing, software
10
//        distributed under the License is distributed on an "AS IS" BASIS,
11
//        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//        See the License for the specific language governing permissions and
13
//        limitations under the License.
14
package cmd
15

16
import (
17
        "context"
18
        "encoding/json"
19
        "fmt"
20
        "os"
21
        "os/exec"
22
        "path/filepath"
23
        "strings"
24

25
        "github.com/pkg/errors"
26
        "github.com/spf13/cobra"
27
        "github.com/spf13/viper"
28

29
        "github.com/mendersoftware/mender-server/services/create-artifact-worker/client"
30
        "github.com/mendersoftware/mender-server/services/create-artifact-worker/config"
31
        mlog "github.com/mendersoftware/mender-server/services/create-artifact-worker/log"
32
)
33

34
const (
35
        argToken          = "token"
36
        argArtifactName   = "artifact-name"
37
        argDescription    = "description"
38
        argDeviceType     = "device-type"
39
        argArtifactId     = "artifact-id"
40
        argGetArtifactUri = "get-artifact-uri"
41
        argDelArtifactUri = "delete-artifact-uri"
42
        argTenantId       = "tenant-id"
43
        argArgs           = "args"
44
)
45

46
type args struct {
47
        Filename           string `json:"filename"`
48
        DestDir            string `json:"dest_dir"`
49
        SoftwareFilesystem string `json:"software_filesystem"`
50
        SoftwareName       string `json:"software_name"`
51
        SoftwareVersion    string `json:"software_version"`
52
}
53

54
var singleFileCmd = &cobra.Command{
55
        Use:   "single-file",
56
        Short: "Generate an update using a single-file update module.",
57
        Long: "\nBesides command line args, supports the following env vars:\n\n" +
58
                "CREATE_ARTIFACT_SKIPVERIFY skip ssl verification (default: false)\n" +
59
                "CREATE_ARTIFACT_WORKDIR working dir for processing (default: /var)\n" +
60
                "CREATE_ARTIFACT_DEPLOYMENTS_URL internal deployments service url\n",
UNCOV
61
        Run: func(cmd *cobra.Command, args []string) {
×
UNCOV
62
                c, err := NewSingleFileCmd(cmd, args)
×
UNCOV
63
                if err != nil {
×
64
                        mlog.Error(err.Error())
×
65
                        os.Exit(1)
×
66
                }
×
67

UNCOV
68
                err = c.Run()
×
UNCOV
69
                if err != nil {
×
70
                        mlog.Error(err.Error())
×
71
                        os.Exit(1)
×
72
                }
×
73
        },
74
}
75

UNCOV
76
func init() {
×
UNCOV
77
        singleFileCmd.Flags().String(argToken, "", "auth token")
×
UNCOV
78
        _ = singleFileCmd.MarkFlagRequired(argToken)
×
UNCOV
79

×
UNCOV
80
        singleFileCmd.Flags().String(argArtifactName, "", "artifact name")
×
UNCOV
81
        _ = singleFileCmd.MarkFlagRequired(argArtifactName)
×
UNCOV
82

×
UNCOV
83
        singleFileCmd.Flags().String(argArtifactId, "", "artifact id")
×
UNCOV
84
        _ = singleFileCmd.MarkFlagRequired(argArtifactId)
×
UNCOV
85

×
UNCOV
86
        singleFileCmd.Flags().String(
×
UNCOV
87
                argGetArtifactUri,
×
UNCOV
88
                "",
×
UNCOV
89
                "pre-signed s3 url to uploaded temp artifact (GET)",
×
UNCOV
90
        )
×
UNCOV
91
        _ = singleFileCmd.MarkFlagRequired(argGetArtifactUri)
×
UNCOV
92

×
UNCOV
93
        singleFileCmd.Flags().String(
×
UNCOV
94
                argDelArtifactUri,
×
UNCOV
95
                "",
×
UNCOV
96
                "pre-signed s3 url to uploaded temp artifact (DELETE)",
×
UNCOV
97
        )
×
UNCOV
98
        _ = singleFileCmd.MarkFlagRequired(argDelArtifactUri)
×
UNCOV
99

×
UNCOV
100
        singleFileCmd.Flags().String(argTenantId, "", "tenant id")
×
UNCOV
101
        _ = singleFileCmd.MarkFlagRequired(argTenantId)
×
UNCOV
102

×
UNCOV
103
        singleFileCmd.Flags().String(argDeviceType, "", "device type")
×
UNCOV
104
        _ = singleFileCmd.MarkFlagRequired(argDeviceType)
×
UNCOV
105

×
UNCOV
106
        // json string of specific args: dest dir, file name
×
UNCOV
107
        singleFileCmd.Flags().String(
×
UNCOV
108
                argArgs,
×
UNCOV
109
                "",
×
UNCOV
110
                "specific args in json form: {\"file\":<DESTINATION_FILE_NAME_ON_DEVICE>,"+
×
UNCOV
111
                        " \"dest_dir\":<DESTINATION_DIR_ON_DEVICE>},"+
×
UNCOV
112
                        " \"software_filesystem\":<SOFTWARE_FILESYSTEM>},"+
×
UNCOV
113
                        " \"software_name\":<SOFTWARE_NAME>},"+
×
UNCOV
114
                        " \"software_version\":<SOFTWARE_VERSION>}",
×
UNCOV
115
        )
×
UNCOV
116
        _ = singleFileCmd.MarkFlagRequired(argArgs)
×
UNCOV
117

×
UNCOV
118
        singleFileCmd.Flags().String(argDescription, "", "artifact description")
×
UNCOV
119
}
×
120

121
type SingleFileCmd struct {
122
        ServerUrl      string
123
        DeploymentsUrl string
124
        SkipVerify     bool
125
        Workdir        string
126

127
        ArtifactName   string
128
        Description    string
129
        DeviceTypes    []string
130
        ArtifactId     string
131
        GetArtifactUri string
132
        DelArtifactUri string
133
        Args           string
134
        TenantId       string
135
        AuthToken      string
136

137
        // type-specific args
138
        FileName           string
139
        DestDir            string
140
        SoftwareFilesystem string
141
        SoftwareName       string
142
        SoftwareVersion    string
143
}
144

UNCOV
145
func NewSingleFileCmd(cmd *cobra.Command, args []string) (*SingleFileCmd, error) {
×
UNCOV
146
        c := &SingleFileCmd{}
×
UNCOV
147

×
UNCOV
148
        if err := c.init(cmd); err != nil {
×
149
                return nil, err
×
150
        }
×
151

UNCOV
152
        if err := c.Validate(); err != nil {
×
153
                return nil, err
×
154
        }
×
155

UNCOV
156
        return c, nil
×
157
}
158

UNCOV
159
func (c *SingleFileCmd) init(cmd *cobra.Command) error {
×
UNCOV
160
        c.DeploymentsUrl = viper.GetString(config.CfgDeploymentsUrl)
×
UNCOV
161
        c.SkipVerify = viper.GetBool(config.CfgSkipVerify)
×
UNCOV
162
        c.Workdir = viper.GetString(config.CfgWorkDir)
×
UNCOV
163

×
UNCOV
164
        var arg string
×
UNCOV
165
        arg, err := cmd.Flags().GetString(argArtifactName)
×
UNCOV
166
        c.ArtifactName = arg
×
UNCOV
167
        if err != nil {
×
168
                return err
×
169
        }
×
170

UNCOV
171
        arg, err = cmd.Flags().GetString(argDescription)
×
UNCOV
172
        c.Description = arg
×
UNCOV
173
        if err != nil {
×
174
                return err
×
175
        }
×
176

UNCOV
177
        arg, err = cmd.Flags().GetString(argDeviceType)
×
UNCOV
178
        c.DeviceTypes = strings.Split(arg, ",")
×
UNCOV
179
        if err != nil {
×
180
                return err
×
181
        }
×
182

UNCOV
183
        arg, err = cmd.Flags().GetString(argArtifactId)
×
UNCOV
184
        c.ArtifactId = arg
×
UNCOV
185
        if err != nil {
×
186
                return err
×
187
        }
×
188

UNCOV
189
        arg, err = cmd.Flags().GetString(argGetArtifactUri)
×
UNCOV
190
        c.GetArtifactUri = arg
×
UNCOV
191
        if err != nil {
×
192
                return err
×
193
        }
×
194

UNCOV
195
        arg, err = cmd.Flags().GetString(argDelArtifactUri)
×
UNCOV
196
        c.DelArtifactUri = arg
×
UNCOV
197
        if err != nil {
×
198
                return err
×
199
        }
×
200

UNCOV
201
        arg, err = cmd.Flags().GetString(argTenantId)
×
UNCOV
202
        c.TenantId = arg
×
UNCOV
203
        if err != nil {
×
204
                return err
×
205
        }
×
206

UNCOV
207
        arg, err = cmd.Flags().GetString(argToken)
×
UNCOV
208
        c.AuthToken = arg
×
UNCOV
209
        if err != nil {
×
210
                return err
×
211
        }
×
212

UNCOV
213
        arg, err = cmd.Flags().GetString(argArgs)
×
UNCOV
214
        c.Args = arg
×
UNCOV
215
        if err != nil {
×
216
                return err
×
217
        }
×
218

UNCOV
219
        return nil
×
220
}
221

UNCOV
222
func (c *SingleFileCmd) Validate() error {
×
UNCOV
223
        if err := config.ValidAbsPath(c.Workdir); err != nil {
×
224
                return errors.Wrap(err, "invalid workdir")
×
225
        }
×
226

UNCOV
227
        var args args
×
UNCOV
228

×
UNCOV
229
        err := json.Unmarshal([]byte(c.Args), &args)
×
UNCOV
230
        if err != nil {
×
231
                return errors.Wrap(err, "can't parse 'args'")
×
232
        }
×
233

UNCOV
234
        c.FileName = args.Filename
×
UNCOV
235
        c.DestDir = args.DestDir
×
UNCOV
236
        c.SoftwareFilesystem = args.SoftwareFilesystem
×
UNCOV
237
        c.SoftwareName = args.SoftwareName
×
UNCOV
238
        c.SoftwareVersion = args.SoftwareVersion
×
UNCOV
239

×
UNCOV
240
        if c.FileName == "" {
×
241
                return errors.New("destination filename can't be empty")
×
242
        }
×
243

UNCOV
244
        if err := config.ValidAbsPath(c.DestDir); err != nil {
×
245
                return errors.Wrap(err, "invalid artifact destination dir")
×
246
        }
×
247

UNCOV
248
        return nil
×
249
}
250

UNCOV
251
func (c *SingleFileCmd) Run() error {
×
UNCOV
252
        mlog.Info("running single-file update module generation:\n%s", c.dumpArgs())
×
UNCOV
253
        mlog.Info("config:\n%s", config.Dump())
×
UNCOV
254

×
UNCOV
255
        cd, err := client.NewDeployments(c.DeploymentsUrl, c.SkipVerify)
×
UNCOV
256
        if err != nil {
×
257
                return errors.New("failed to configure 'deployments' client")
×
258
        }
×
259

UNCOV
260
        cs3 := client.NewStorage(c.SkipVerify)
×
UNCOV
261

×
UNCOV
262
        ctx := context.Background()
×
UNCOV
263

×
UNCOV
264
        mlog.Verbose("creating temp dir at", c.Workdir)
×
UNCOV
265

×
UNCOV
266
        downloadDir, err := os.MkdirTemp(c.Workdir, "single-file")
×
UNCOV
267
        if err != nil {
×
268
                return errors.Wrapf(err, "failed to create temp dir under workdir %s", c.Workdir)
×
269
        }
×
270

271
        //gotcha: must download under the correct name (destination name on the device)
272
        //artifact generator will not allow renaming it
UNCOV
273
        downloadFile := filepath.Join(downloadDir, c.FileName)
×
UNCOV
274

×
UNCOV
275
        mlog.Verbose("downloading temp artifact to %s", downloadFile)
×
UNCOV
276

×
UNCOV
277
        err = cs3.Download(ctx, c.GetArtifactUri, downloadFile)
×
UNCOV
278
        if err != nil {
×
279
                return errors.Wrapf(err, "failed to download input file at %s", c.GetArtifactUri)
×
280
        }
×
281

282
        // make the filename unique by naming it after the artifact
UNCOV
283
        outfile := c.ArtifactId + "-generated"
×
UNCOV
284
        outfile = filepath.Join(downloadDir, outfile)
×
UNCOV
285

×
UNCOV
286
        mlog.Verbose("generating output artifact %s", outfile)
×
UNCOV
287

×
UNCOV
288
        // run gen script
×
UNCOV
289
        args := []string{
×
UNCOV
290
                "-n", c.ArtifactName,
×
UNCOV
291
                "-d", c.DestDir,
×
UNCOV
292
                "-o", outfile,
×
UNCOV
293
        }
×
UNCOV
294
        if c.SoftwareFilesystem != "" {
×
295
                args = append(args, "--software-filesystem", c.SoftwareFilesystem)
×
296
        }
×
UNCOV
297
        if c.SoftwareName != "" {
×
298
                args = append(args, "--software-name", c.SoftwareName)
×
299
        }
×
UNCOV
300
        if c.SoftwareVersion != "" {
×
301
                args = append(args, "--software-version", c.SoftwareVersion)
×
302
        }
×
303

UNCOV
304
        for _, deviceType := range c.DeviceTypes {
×
UNCOV
305
                args = append(args, "-t", deviceType)
×
UNCOV
306
        }
×
UNCOV
307
        args = append(args, downloadFile)
×
UNCOV
308
        cmd := exec.Command("/usr/bin/single-file-artifact-gen", args...)
×
UNCOV
309

×
UNCOV
310
        std, err := cmd.CombinedOutput()
×
UNCOV
311
        mlog.Info(string(std))
×
UNCOV
312
        if err != nil {
×
313
                return errors.Wrapf(err, "single-file-artifact-gen exited with error %s", std)
×
314
        }
×
315

UNCOV
316
        mlog.Verbose("deleting temp file from S3")
×
UNCOV
317

×
UNCOV
318
        err = cs3.Delete(ctx, c.DelArtifactUri)
×
UNCOV
319
        if err != nil {
×
320
                return errors.Wrapf(err, "failed to delete artifact at %s", c.DelArtifactUri)
×
321
        }
×
322

UNCOV
323
        mlog.Verbose("uploading generated artifact")
×
UNCOV
324
        err = cd.UploadArtifactInternal(ctx, outfile, c.ArtifactId, c.TenantId, c.Description)
×
UNCOV
325
        if err != nil {
×
326
                return errors.Wrapf(err, "failed to upload generated artifact")
×
327
        }
×
328

UNCOV
329
        err = os.RemoveAll(downloadDir)
×
UNCOV
330
        if err != nil {
×
331
                mlog.Error("failed to remove temp working dir %s: %v", downloadDir, err.Error())
×
332
        }
×
333

UNCOV
334
        return nil
×
335
}
336

UNCOV
337
func (c *SingleFileCmd) dumpArgs() string {
×
UNCOV
338
        return dumpArg(argArtifactName, c.ArtifactName) +
×
UNCOV
339
                dumpArg(argDescription, c.Description) +
×
UNCOV
340
                dumpArg(argArtifactId, c.ArtifactId) +
×
UNCOV
341
                dumpArg(argDeviceType, strings.Join(c.DeviceTypes, ",")) +
×
UNCOV
342
                dumpArg(argTenantId, c.TenantId) +
×
UNCOV
343
                dumpArg(argGetArtifactUri, c.GetArtifactUri) +
×
UNCOV
344
                dumpArg(argDelArtifactUri, c.DelArtifactUri) +
×
UNCOV
345
                dumpArg(argArgs, c.Args)
×
UNCOV
346
}
×
347

UNCOV
348
func dumpArg(n, v string) string {
×
UNCOV
349
        return fmt.Sprintf("--%s: %s\n", n, v)
×
UNCOV
350
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc