/
apk.go
144 lines (119 loc) · 3.57 KB
/
apk.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
package apk
import (
"archive/tar"
"compress/gzip"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"github.com/chainguard-dev/go-apk/pkg/apk"
"github.com/go-ini/ini"
"github.com/wolfi-dev/wolfictl/pkg/versions"
)
type Context struct {
client *http.Client
indexURL string
}
func New(client *http.Client, indexURL string) Context {
return Context{
client: client,
indexURL: indexURL,
}
}
func (c Context) GetApkPackages() (map[string]*apk.Package, error) {
req, err := http.NewRequest("GET", c.indexURL, nil)
if err != nil {
return nil, err
}
resp, err := c.client.Do(req)
if err != nil {
return nil, fmt.Errorf("failed getting URI %s: %w", c.indexURL, err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("non ok http response for URI %s code: %v", c.indexURL, resp.StatusCode)
}
return ParseApkIndex(resp.Body)
}
func ParsePackageIndexFromJSON(jsonAPKLIndex []byte) (map[string]*apk.Package, error) {
var idx apk.APKIndex
wolfiPackages := make(map[string]*apk.Package)
err := json.Unmarshal(jsonAPKLIndex, &idx)
if err != nil {
return nil, err
}
return getLatestPackagesMap(idx.Packages, wolfiPackages)
}
func ParseUnpackedApkIndex(indexData io.ReadCloser) (map[string]*apk.Package, error) {
wolfiPackages := make(map[string]*apk.Package)
packages, err := apk.ParsePackageIndex(indexData)
if err != nil {
return nil, fmt.Errorf("failed to parse response %v: %w", indexData, err)
}
return getLatestPackagesMap(packages, wolfiPackages)
}
func getLatestPackagesMap(apkIndexPackages []*apk.Package, wolfiPackages map[string]*apk.Package) (map[string]*apk.Package, error) {
for _, p := range apkIndexPackages {
if wolfiPackages[p.Name] != nil {
existingPackageVersion, err := versions.NewVersion(wolfiPackages[p.Name].Version)
if err != nil {
return nil, fmt.Errorf("failed to create a version for package %s from %s: %w", p.Name, wolfiPackages[p.Name].Version, err)
}
apkPackageVersion, err := versions.NewVersion(p.Version)
if err != nil {
return nil, fmt.Errorf("failed to create a new version for package %s from %s: %w", p.Name, p.Version, err)
}
// replace in our map if we find a newer version in the APKINDEX
if existingPackageVersion.LessThan(apkPackageVersion) {
wolfiPackages[p.Name] = p
}
} else {
wolfiPackages[p.Name] = p
}
}
log.Printf("found %d latest apk index package versions", len(wolfiPackages))
return wolfiPackages, nil
}
func ParseApkIndex(indexData io.ReadCloser) (map[string]*apk.Package, error) {
wolfiPackages := make(map[string]*apk.Package)
apkIndex, err := apk.IndexFromArchive(indexData)
if err != nil {
return nil, fmt.Errorf("failed to parse response %v: %w", indexData, err)
}
return getLatestPackagesMap(apkIndex.Packages, wolfiPackages)
}
func PKGINFOFromAPK(r io.Reader) (*apk.Package, error) {
gr, err := gzip.NewReader(r)
if err != nil {
return nil, fmt.Errorf("failed to create gzip reader: %w", err)
}
tr := tar.NewReader(gr)
var pkginfoHdr *tar.Header
for {
hdr, err := tr.Next()
if err == io.EOF {
break
}
if err != nil {
return nil, fmt.Errorf("failed to read tar header: %w", err)
}
if hdr.Name == ".PKGINFO" {
pkginfoHdr = hdr
break
}
}
if pkginfoHdr == nil {
return nil, fmt.Errorf("failed to find .PKGINFO in apk")
}
pkginfo := new(apk.Package)
loaded, err := ini.Load(tr)
if err != nil {
return nil, fmt.Errorf("failed to parse INI data: %w", err)
}
err = loaded.MapTo(pkginfo)
if err != nil {
return nil, fmt.Errorf("failed to map INI data to struct: %w", err)
}
return pkginfo, nil
}