-
Notifications
You must be signed in to change notification settings - Fork 5
/
util_package_uploader.go
151 lines (128 loc) · 5.3 KB
/
util_package_uploader.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// util_package_uploader.go
package jamfpro
import (
"context"
"fmt"
"os"
"path/filepath"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/feature/s3/manager"
"github.com/aws/aws-sdk-go-v2/service/s3"
)
// DoPackageUpload creates a new file in JCDS 2.0 using AWS SDK v2
func (c *Client) DoPackageUpload(filePath string, packageData *ResourcePackage) (*ResponseJCDS2File, *ResponsePackageCreatedAndUpdated, error) {
// Step 1: Obtain AWS credentials for the package upload endpoint
var uploadCredentials ResponseJCDS2UploadCredentials
resp, err := c.HTTP.DoRequest("POST", uriJCDS2+"/files", nil, &uploadCredentials)
if err != nil {
return nil, nil, fmt.Errorf("failed to obtain upload credentials: %v", err)
}
if resp != nil && resp.Body != nil {
defer resp.Body.Close()
}
// Validate if we received necessary details
if uploadCredentials.Region == "" || uploadCredentials.BucketName == "" || uploadCredentials.Path == "" {
return nil, nil, fmt.Errorf("incomplete upload credentials received")
}
// Step 2: Use the obtained credentials to configure AWS SDK v2
cfg, err := config.LoadDefaultConfig(context.TODO(),
config.WithRegion(uploadCredentials.Region),
config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(uploadCredentials.AccessKeyID, uploadCredentials.SecretAccessKey, uploadCredentials.SessionToken)),
)
if err != nil {
return nil, nil, fmt.Errorf("failed to create AWS config: %v", err)
}
// Create S3 service client
s3Client := s3.NewFromConfig(cfg)
// Step 3: Create an Uploader with the configuration and default options
uploader := manager.NewUploader(s3Client)
// Open the file and use a progressReader to track the upload progress
file, err := os.Open(filePath)
if err != nil {
return nil, nil, fmt.Errorf("failed to open file: %v", err)
}
defer file.Close()
fileInfo, err := file.Stat()
if err != nil {
return nil, nil, fmt.Errorf("failed to get file info: %v", err)
}
progressFn := func(read, total int64, unit string) {
fmt.Printf("\rUploaded %d / %d %s (%.2f%%)", read, total, unit, float64(read)/float64(total)*100)
}
reader := &progressReader{
reader: file,
totalBytes: fileInfo.Size(),
progressFn: progressFn,
}
// Create the upload input
uploadInput := &s3.PutObjectInput{
Bucket: aws.String(uploadCredentials.BucketName),
Key: aws.String(uploadCredentials.Path + filepath.Base(filePath)),
Body: reader,
}
// Step 4. Perform the upload
_, err = uploader.Upload(context.TODO(), uploadInput)
if err != nil {
return nil, nil, fmt.Errorf("failed to upload file: %v", err)
}
fmt.Println("\nUpload completed Successfully")
// Step 5. Upload package metadata to Jamf Pro
pkgName := filepath.Base(filePath)
pkg := ResourcePackage{
Name: packageData.Name,
Filename: pkgName,
Category: packageData.Category,
Info: packageData.Info,
Notes: packageData.Notes,
Priority: packageData.Priority,
RebootRequired: packageData.RebootRequired,
FillUserTemplate: packageData.FillUserTemplate,
FillExistingUsers: packageData.FillExistingUsers,
BootVolumeRequired: packageData.BootVolumeRequired,
AllowUninstalled: packageData.AllowUninstalled,
OSRequirements: packageData.OSRequirements,
RequiredProcessor: packageData.RequiredProcessor,
SwitchWithPackage: packageData.SwitchWithPackage,
InstallIfReportedAvailable: packageData.InstallIfReportedAvailable,
ReinstallOption: packageData.ReinstallOption,
TriggeringFiles: packageData.TriggeringFiles,
SendNotification: packageData.SendNotification,
}
// Step 5. Upload package metadata to Jamf Pro
metadataResponse, err := c.CreatePackage(pkg)
if err != nil {
return nil, nil, fmt.Errorf("failed to create package metadata in Jamf Pro: %v", err)
}
// Log the package creation response from Jamf Pro
fmt.Printf("Jamf Pro package metadata created successfully with package ID: %d\n", metadataResponse.ID)
// Construct the final file upload response
packageUploadresponse := &ResponseJCDS2File{
URI: fmt.Sprintf("s3://%s/%s%s", uploadCredentials.BucketName, uploadCredentials.Path, filepath.Base(filePath)),
}
// Construct the jamf pro package creation response
jamfPackageMetaData := &ResponsePackageCreatedAndUpdated{
ID: metadataResponse.ID,
}
// Return the file upload response, the package creation response, and nil for no error
return packageUploadresponse, jamfPackageMetaData, nil
}
// Read implements the io.Reader interface for progressReader, reporting upload progress in kilobytes and megabytes.
func (r *progressReader) UploadProgress(p []byte) (int, error) {
n, err := r.reader.Read(p)
r.readBytes += int64(n)
// Report progress in more human-readable units (KB or MB)
const kb = 1024
const mb = 1024 * kb
readKB := r.readBytes / kb
totalKB := r.totalBytes / kb
if totalKB > kb { // If the total size is larger than 1 MB, report in MB
readMB := r.readBytes / mb
totalMB := r.totalBytes / mb
r.progressFn(readMB, totalMB, "MB")
} else { // For smaller files, report in KB
r.progressFn(readKB, totalKB, "KB")
}
return n, err
}