Skip to content

Commit 24360e4

Browse files
authored
Merge pull request #83 from sapcc/github-releases
Add support for github-releases
2 parents 7cd8508 + 4ce3d77 commit 24360e4

File tree

337 files changed

+97843
-36
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

337 files changed

+97843
-36
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
/build
33

44
# for my own testing
5+
/.env
56
/*.yaml
67
!/Makefile.maker.yaml

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# v2.9.0 (TBD)
22

3+
New features:
4+
- Add support for GitHub releases.
5+
36
Bugfixes:
47
- Fixed transfer of objects from swift source when object name is not a well-formed path. For example, an object name like "a///b" is not wrongly normalized into "a/b" anymore.
58

README.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
* [Usage](#usage)
88
* [Alternative authentication options](#alternative-authentication-options)
99
* [Source specification](#source-specification)
10+
* [Yum](#yum)
11+
* [Debian](#debian)
12+
* [Github Releases](#github-releases)
13+
* [Swift](#swift)
1014
* [File selection](#file-selection)
1115
* [By name](#by-name)
1216
* [By age](#by-age)
@@ -238,6 +242,43 @@ jobs:
238242
object_prefix: ubuntu
239243
```
240244

245+
#### Github Releases
246+
247+
If `jobs[].from.url` refers to a GitHub repository, setting `jobs[].from.type` to
248+
`github-releases` will cause `swift-http-import` to use GitHub's API to discover and
249+
download GitHub releases for the repository. Repositories hosted on a GitHub Enterprise
250+
instance are also supported.
251+
252+
Since GitHub's API rate limits the number of requests per IP therefore it is
253+
**recommended** that you specify a GitHub personal access token in the `jobs[].from.token`
254+
field. Refer to the [GitHub API docs](https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting)
255+
for more info on its rate limiting. This field is **required** if the repository is hosted on a Github Enterprise instance instead of `github.com`.
256+
Instead of providing your token as plain text in the config file, you can use the
257+
`fromEnv` special syntax for the `jobs[].from.token` field. See [Alternative
258+
authentication options](#alternative-authentication-options) for more details.
259+
260+
If a repository publishes GitHub releases using different tags, e.g. server components at
261+
`server-x.y.z` and client at `client-x.y.z`, and you only want to get releases whose tag
262+
matches a specific pattern then you can specify a regex for this in the
263+
`jobs[].from.tag_name_pattern` field. Only releases whose tag name matches this regex will
264+
be transferred.
265+
266+
Prerelease and draft releases are not transferred by default. You can override this
267+
behavior by setting the `jobs[].from.include_prerelease` and `jobs[].from.include_draft`
268+
field to `true`.
269+
270+
[Link to full example config file](./examples/source-debian.yaml)
271+
272+
```yaml
273+
jobs:
274+
- from:
275+
url: https://github.com/sapcc/limesctl
276+
type: github-releases
277+
to:
278+
container: mirror
279+
object_prefix: sapcc/limesctl
280+
```
281+
241282
#### Swift
242283

243284
Alternatively, the source in `jobs[].from` can also be a private Swift container if Swift credentials are specified
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
swift:
2+
auth_url: https://my.keystone.local:5000/v3
3+
user_name: uploader
4+
user_domain_name: Default
5+
project_name: datastore
6+
project_domain_name: Default
7+
password: 20g82rzg235oughq
8+
9+
jobs:
10+
- from:
11+
url: https://github.com/sapcc/limesctl
12+
type: github-releases
13+
token: ghp_asjdkajsdlbyaksjd2
14+
tag_name_pattern: "^v[0-9]+.[0-9]+.[0-9]+$"
15+
include_draft: false
16+
include_prerelease: false
17+
to:
18+
container: github
19+
object_prefix: sapcc/limesctl
20+
match:
21+
not_older_than: 12 weeks

go.mod

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,24 @@ go 1.17
44

55
require (
66
github.com/cactus/go-statsd-client/v4 v4.0.0
7+
github.com/google/go-github/v44 v44.0.0
78
github.com/gophercloud/gophercloud v0.24.0
89
github.com/gophercloud/utils v0.0.0-20220307143606-8e7800759d16
910
github.com/majewsky/schwift v1.1.0
1011
github.com/sapcc/go-bits v0.0.0-20220517080651-baba74caa927
1112
github.com/ulikunitz/xz v0.5.10
1213
golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898
1314
golang.org/x/net v0.0.0-20220517181318-183a9ca12b87
15+
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be
1416
gopkg.in/yaml.v2 v2.4.0
1517
pault.ag/go/debian v0.12.0
1618
)
1719

1820
require (
21+
github.com/golang/protobuf v1.5.2 // indirect
22+
github.com/google/go-querystring v1.1.0 // indirect
1923
github.com/jpillora/longestcommon v0.0.0-20161227235612-adb9d91ee629 // indirect
24+
google.golang.org/appengine v1.6.7 // indirect
25+
google.golang.org/protobuf v1.28.0 // indirect
2026
pault.ag/go/topsort v0.0.0-20160530003732-f98d2ad46e1a // indirect
2127
)

go.sum

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
github.com/DataDog/zstd v1.4.8/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
22
github.com/cactus/go-statsd-client/v4 v4.0.0 h1:cjO9CI3GAHtj/Vmmt1/BRq8+hf5MXy/Pr/CM6Dy4dp0=
33
github.com/cactus/go-statsd-client/v4 v4.0.0/go.mod h1:m73kwJp6TN0Ja9P6ycdZhWM1MlfxY/95WZ//IptPQ+Y=
4+
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
5+
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
6+
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
7+
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
8+
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
9+
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
10+
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
11+
github.com/google/go-github/v44 v44.0.0 h1:1Lfk2mhM7pTWqwGC6Ft16S3c2LBw8DLcw9TOhYoQ9zE=
12+
github.com/google/go-github/v44 v44.0.0/go.mod h1:CqZYQRxOcb81M+ufZB7duWNS0lFfas/r7cEAKpLBYww=
13+
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
14+
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
415
github.com/gophercloud/gophercloud v0.15.1-0.20210202035223-633d73521055/go.mod h1:wRtmUelyIIv3CSSDI47aUwbs075O6i+LY+pXsKCBsb4=
516
github.com/gophercloud/gophercloud v0.17.0/go.mod h1:wRtmUelyIIv3CSSDI47aUwbs075O6i+LY+pXsKCBsb4=
617
github.com/gophercloud/gophercloud v0.20.0/go.mod h1:wRtmUelyIIv3CSSDI47aUwbs075O6i+LY+pXsKCBsb4=
@@ -29,9 +40,12 @@ golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0
2940
golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898 h1:SLP7Q4Di66FONjDJbCYrCRrh97focO6sLogHO7/g8F0=
3041
golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
3142
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
43+
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
3244
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
3345
golang.org/x/net v0.0.0-20220517181318-183a9ca12b87 h1:cCR+9mKLOGyX4Zx+uBZDXEDAQsvKQ/XbW4vreG5v1jU=
3446
golang.org/x/net v0.0.0-20220517181318-183a9ca12b87/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
47+
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
48+
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
3549
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
3650
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
3751
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -41,8 +55,17 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
4155
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
4256
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
4357
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
58+
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
4459
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
4560
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
61+
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
62+
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
63+
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
64+
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
65+
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
66+
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
67+
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
68+
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
4669
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
4770
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
4871
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

pkg/objects/config.go

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,8 @@ func (u *SourceUnmarshaler) UnmarshalYAML(unmarshal func(interface{}) error) err
199199
u.Source = &YumSource{}
200200
case "debian":
201201
u.Source = &DebianSource{}
202+
case "github-releases":
203+
u.Source = &GithubReleaseSource{}
202204
default:
203205
return fmt.Errorf("unexpected value: type = %q", probe.Type)
204206
}
@@ -219,10 +221,11 @@ type Job struct {
219221

220222
//Compile validates the given JobConfiguration, then creates and prepares a Job from it.
221223
func (cfg JobConfiguration) Compile(name string, swift SwiftLocation) (job *Job, errors []error) {
222-
if cfg.Source.Source == nil {
224+
jobSrc := cfg.Source.Source
225+
if jobSrc == nil {
223226
errors = append(errors, fmt.Errorf("missing value for %s.from", name))
224227
} else {
225-
errors = append(errors, cfg.Source.Source.Validate(name+".from")...)
228+
errors = append(errors, jobSrc.Validate(name+".from")...)
226229
}
227230
if cfg.Target == nil {
228231
errors = append(errors, fmt.Errorf("missing value for %s.to", name))
@@ -242,27 +245,28 @@ func (cfg JobConfiguration) Compile(name string, swift SwiftLocation) (job *Job,
242245
}
243246

244247
if cfg.Match.NotOlderThan != nil {
245-
_, isSwiftSource := cfg.Source.Source.(*SwiftLocation)
246-
if !isSwiftSource {
247-
errors = append(errors, fmt.Errorf("invalid value for %s.match.not_older_than: this option is not supported for source type %T", name, cfg.Source.Source))
248+
_, isSwiftSource := jobSrc.(*SwiftLocation)
249+
_, isGitHubReleaseSource := jobSrc.(*GithubReleaseSource)
250+
if !isSwiftSource && !isGitHubReleaseSource {
251+
errors = append(errors, fmt.Errorf("invalid value for %s.match.not_older_than: this option is not supported for source type %T", name, jobSrc))
248252
}
249253
}
250254

251255
if cfg.Match.SimplisticComparison != nil {
252-
_, isURLSource := cfg.Source.Source.(*URLSource)
253-
_, isSwiftSource := cfg.Source.Source.(*SwiftLocation)
256+
_, isURLSource := jobSrc.(*URLSource)
257+
_, isSwiftSource := jobSrc.(*SwiftLocation)
254258
if !isURLSource && !isSwiftSource {
255-
errors = append(errors, fmt.Errorf("invalid value for %s.match.simplistic_comparison: this option is not supported for source type %T", name, cfg.Source.Source))
259+
errors = append(errors, fmt.Errorf("invalid value for %s.match.simplistic_comparison: this option is not supported for source type %T", name, jobSrc))
256260
}
257261
}
258262

259-
_, isYumSource := cfg.Source.Source.(*YumSource)
263+
_, isYumSource := jobSrc.(*YumSource)
260264
if isYumSource {
261-
cfg.Source.Source.(*YumSource).gpgKeyRing = cfg.gpgKeyRing
265+
jobSrc.(*YumSource).gpgKeyRing = cfg.gpgKeyRing
262266
}
263-
_, isDebianSource := cfg.Source.Source.(*DebianSource)
267+
_, isDebianSource := jobSrc.(*DebianSource)
264268
if isDebianSource {
265-
cfg.Source.Source.(*DebianSource).gpgKeyRing = cfg.gpgKeyRing
269+
jobSrc.(*DebianSource).gpgKeyRing = cfg.gpgKeyRing
266270
}
267271

268272
if cfg.Segmenting != nil {
@@ -289,7 +293,7 @@ func (cfg JobConfiguration) Compile(name string, swift SwiftLocation) (job *Job,
289293
}
290294

291295
job = &Job{
292-
Source: cfg.Source.Source,
296+
Source: jobSrc,
293297
Target: cfg.Target,
294298
Segmenting: cfg.Segmenting,
295299
Expiration: cfg.Expiration,
@@ -316,6 +320,10 @@ func (cfg JobConfiguration) Compile(name string, swift SwiftLocation) (job *Job,
316320
job.Matcher.NotOlderThan = &cutoff
317321
}
318322
job.Matcher.SimplisticComparison = cfg.Match.SimplisticComparison
323+
_, isGitHubReleaseSource := jobSrc.(*GithubReleaseSource)
324+
if isGitHubReleaseSource {
325+
jobSrc.(*GithubReleaseSource).notOlderThan = job.Matcher.NotOlderThan
326+
}
319327

320328
//do not try connecting to Swift if credentials are invalid etc.
321329
if len(errors) > 0 {

pkg/objects/debian.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,15 +83,12 @@ func (s *DebianSource) Connect(name string) error {
8383

8484
//ListEntries implements the Source interface.
8585
func (s *DebianSource) ListEntries(directoryPath string) ([]FileSpec, *ListEntriesError) {
86-
return nil, &ListEntriesError{
87-
Location: s.urlSource.getURLForPath(directoryPath).String(),
88-
Message: "ListEntries is not implemented for DebianSource",
89-
}
86+
return nil, ErrListEntriesNotSupported
9087
}
9188

9289
//GetFile implements the Source interface.
93-
func (s *DebianSource) GetFile(directoryPath string, requestHeaders schwift.ObjectHeaders) (body io.ReadCloser, sourceState FileState, err error) {
94-
return s.urlSource.GetFile(directoryPath, requestHeaders)
90+
func (s *DebianSource) GetFile(path string, requestHeaders schwift.ObjectHeaders) (body io.ReadCloser, sourceState FileState, err error) {
91+
return s.urlSource.GetFile(path, requestHeaders)
9592
}
9693

9794
//ListAllFiles implements the Source interface.

pkg/objects/file.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,12 @@ type File struct {
4242
//downloaded contents and metadata in the remaining fields of the FileSpec to
4343
//avoid double download.
4444
type FileSpec struct {
45-
Path string
46-
IsDirectory bool
47-
//only set for files in Swift sources (otherwise nil)
45+
Path string
46+
// DownloadPath is set for files whose path for downloading their contents is
47+
// different from the Path variable which denotes their actual file path.
48+
DownloadPath string
49+
IsDirectory bool
50+
//only set for files in Swift and GitHub release sources (otherwise nil)
4851
LastModified *time.Time
4952
//only set for symlinks (refers to a path below the ObjectPrefix in the same container)
5053
SymlinkTargetPath string
@@ -154,7 +157,11 @@ func (f File) PerformTransfer() (TransferResult, int64) {
154157
sourceState FileState
155158
)
156159
if f.Spec.Contents == nil {
157-
body, sourceState, err = f.Job.Source.GetFile(f.Spec.Path, requestHeaders)
160+
path := f.Spec.Path
161+
if f.Spec.DownloadPath != "" {
162+
path = f.Spec.DownloadPath
163+
}
164+
body, sourceState, err = f.Job.Source.GetFile(path, requestHeaders)
158165
} else {
159166
logg.Debug("using cached contents for %s", f.Spec.Path)
160167
body, sourceState, err = f.Spec.toTransferFormat(requestHeaders)

0 commit comments

Comments
 (0)