Skip to content

Commit

Permalink
Merge pull request #7 from skip77/main
Browse files Browse the repository at this point in the history
  • Loading branch information
mstg authored Feb 24, 2023
2 parents 06ffd1e + 0ea6123 commit 2c6d6f0
Show file tree
Hide file tree
Showing 5 changed files with 211 additions and 53 deletions.
42 changes: 39 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
# srpmproc
Upstream package importer with auto patching. Reference implementation for OpenPatch

# Usage
## Usage
```
Usage:
srpmproc [flags]
srpmproc [command]
Available Commands:
fetch
fetch
help Help about any command
Flags:
--basic-password string Basic auth password
--basic-username string Basic auth username
--branch-prefix string Branch prefix (replaces import-branch-prefix) (default "r")
--branch-suffix string Branch suffix to use for imported branches
--cdn-url string CDN URL to download blobs from (default "https://git.centos.org/sources")
--cdn string CDN URL shortcuts for well-known distros, auto-assigns --cdn-url. Valid values: rocky8, rocky, fedora, centos, centos-stream. Setting this overrides --cdn-url
--cdn-url string CDN URL to download blobs from. Simple URL follows default rocky/centos patterns. Can be customized using macros (see docs) (default "https://git.centos.org/sources")
--git-committer-email string Email of committer (default "[email protected]")
--git-committer-name string Name of committer (default "rockyautomation")
-h, --help help for srpmproc
Expand All @@ -28,16 +29,51 @@ Flags:
--no-dup-mode If enabled, skips already imported tags
--no-storage-download If enabled, blobs are always downloaded from upstream
--no-storage-upload If enabled, blobs are not uploaded to blob storage
--package-release string Package release to fetch
--package-version string Package version to fetch
--rpm-prefix string Where to retrieve SRPM content. Only used when source-rpm is not a local file (default "https://git.centos.org/rpms")
--single-tag string If set, only this tag is imported
--source-rpm string Location of RPM to process
--ssh-key-location string Location of the SSH key to use to authenticate against upstream
--ssh-user string SSH User (default "git")
--storage-addr string Bucket to use as blob storage
--strict-branch-mode If enabled, only branches with the calculated name are imported and not prefix only
--taglessmode Tagless mode: If set, pull the latest commit from the branch and determine version numbers from spec file. This is auto-tried if tags aren't found.
--tmpfs-mode string If set, packages are imported to path and patched but not pushed
--upstream-prefix string Upstream git repository prefix
--version int Upstream version
Use "srpmproc [command] --help" for more information about a command.
```

<br />

## Examples:

1. Import the kernel package from git.centos.org/rpms/, to local folder /opt/gitroot/rpms/kernel.git/ . Download the lookaside source tarballs from the default CentOS file server location to local folder `/opt/fake_s3/` . We want to grab branch "c8" (import prefix plus RHEL version), and it will be committed as branch "r8" (branch prefix plus RHEL version). This assumes that `/opt/fake_s3` exists, and `/opt/gitroot/rpms/kernel.git` exists and is a git repository of some kind (even an empty one).

```
srpmproc --branch-prefix "r" --import-branch-prefix "c" --rpm-prefix "https://git.centos.org/rpms" --version 8 --storage-addr file:///opt/fake_s3 --upstream-prefix file:///opt/gitroot --cdn centos --strict-branch-mode --source-rpm kernel
```

<br />

## CDN and --cdn-url
The --cdn-url option allows for Go-style templates to craft complex URL patterns. These templates are: `{{.Name}}` (package name), `{{.Hash}}` (hash of lookaside file), `{{.Hashtype}}` (hash type of file, like "sha256" or "sha512"), `{{.Branch}}` (the branch we are importing), and `{{.Filename}}` (the lookaside file's name as it appears in SOURCES/). You can add these values as part of --cdn-url to craft your lookaside pattern.


For example, if I wanted my lookaside downloads to come from CentOS 9 Stream, I would use as part of my command:
```
--cdn-url "https://sources.stream.centos.org/sources/rpms/{{.Name}}/{{.Filename}}/{{.Hashtype}}/{{.Hash}}/{{.Filename}}"
```


**Default Behavior:** If these templates are not used, the default behavior of `--cdn-url` is to fall back on the traditional RHEL import pattern: `<CDN_URL>/<NAME>/<BRANCH>/<HASH>` . If that fails, a further fallback is attempted, the simple: `<CDN_URL>/<HASH>`. These cover the common Rocky Linux and RHEL/CentOS imports if the base lookaside URL is the only thing given. If no `--cdn-url` is specified, it defaults to "https://git.centos.org/sources" (for RHEL imports into Rocky Linux)


**CDN Shorthand:** For convenience, some lookaside patterns for popular distros are provided via the `--cdn` option. You can specify this without needing to use the longer `--cdn-url`. For example, when importing from CentOS 9 Stream, you could use `--cdn centos-stream`





10 changes: 5 additions & 5 deletions cmd/srpmproc/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ var (
packageVersion string
packageRelease string
taglessMode bool
altLookAside bool
cdn string
)

var root = &cobra.Command{
Expand Down Expand Up @@ -96,7 +96,7 @@ func mn(_ *cobra.Command, _ []string) {
PackageVersion: packageVersion,
PackageRelease: packageRelease,
TaglessMode: taglessMode,
AltLookAside: altLookAside,
Cdn: cdn,
})
if err != nil {
log.Fatal(err)
Expand Down Expand Up @@ -131,7 +131,7 @@ func main() {
root.Flags().StringVar(&rpmPrefix, "rpm-prefix", "https://git.centos.org/rpms", "Where to retrieve SRPM content. Only used when source-rpm is not a local file")
root.Flags().StringVar(&importBranchPrefix, "import-branch-prefix", "c", "Import branch prefix")
root.Flags().StringVar(&branchPrefix, "branch-prefix", "r", "Branch prefix (replaces import-branch-prefix)")
root.Flags().StringVar(&cdnUrl, "cdn-url", "https://git.centos.org/sources", "CDN URL to download blobs from")
root.Flags().StringVar(&cdnUrl, "cdn-url", "https://git.centos.org/sources", "CDN URL to download blobs from. Simple URL follows default rocky/centos patterns. Can be customized using macros (see docs)")
root.Flags().StringVar(&singleTag, "single-tag", "", "If set, only this tag is imported")
root.Flags().BoolVar(&noDupMode, "no-dup-mode", false, "If enabled, skips already imported tags")
root.Flags().BoolVar(&moduleMode, "module-mode", false, "If enabled, imports a module instead of a package")
Expand All @@ -146,8 +146,8 @@ func main() {
root.Flags().StringVar(&basicPassword, "basic-password", "", "Basic auth password")
root.Flags().StringVar(&packageVersion, "package-version", "", "Package version to fetch")
root.Flags().StringVar(&packageRelease, "package-release", "", "Package release to fetch")
root.Flags().BoolVar(&taglessMode, "taglessmode", false, "Tagless mode: If set, pull the latest commit from a branch, and determine version info from spec file (aka upstream versions aren't tagged)")
root.Flags().BoolVar(&altLookAside, "altlookaside", false, "If set, uses the new CentOS Stream lookaside pattern (https://<SITE_PREFIX>/<RPM_NAME>/<FILE_NAME>/<SHA_VERSION>/<SHA_SUM>/<FILE_NAME>)")
root.Flags().BoolVar(&taglessMode, "taglessmode", false, "Tagless mode: If set, pull the latest commit from the branch and determine version numbers from spec file. This is auto-tried if tags aren't found.")
root.Flags().StringVar(&cdn, "cdn", "", "CDN URL shortcuts for well-known distros, auto-assigns --cdn-url. Valid values: rocky8, rocky, fedora, centos, centos-stream. Setting this overrides --cdn-url")

if err := root.Execute(); err != nil {
log.Fatal(err)
Expand Down
2 changes: 1 addition & 1 deletion pkg/data/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,5 @@ type ProcessData struct {
PackageVersion string
PackageRelease string
TaglessMode bool
AltLookAside bool
Cdn string
}
115 changes: 85 additions & 30 deletions pkg/modes/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,15 @@
package modes

import (
"bytes"
"fmt"
"io/ioutil"
"log"
"net/http"
"path/filepath"
"sort"
"strings"
"text/template"
"time"

"github.com/go-git/go-git/v5/plumbing/transport"
Expand All @@ -46,6 +49,15 @@ type remoteTarget struct {
when time.Time
}

// Struct to define the possible template values ( {{.Value}} in CDN URL strings:
type Lookaside struct {
Name string
Branch string
Hash string
Hashtype string
Filename string
}

type remoteTargetSlice []remoteTarget

func (p remoteTargetSlice) Len() int {
Expand Down Expand Up @@ -341,44 +353,66 @@ func (g *GitMode) WriteSource(pd *data.ProcessData, md *data.ModeData) error {
} else {

url := ""
// Alternate lookaside logic: if enabled, we pull from a new URL pattern
if !pd.AltLookAside {
url = fmt.Sprintf("%s/%s/%s/%s", pd.CdnUrl, md.Name, branchName, hash)
} else {
// We first need the hash algorithm based on length of hash:
hashType := "sha512"
switch len(hash) {
case 128:
hashType = "sha512"
case 64:
hashType = "sha256"
case 40:
hashType = "sha1"
case 32:
hashType = "md5"
}

// need the name of the file without "SOURCES/":
fileName := strings.Split(path, "/")[1]

// Alt. lookaside url is of the form: <cdn> / <name> / <filename> / <hashtype> / <hash> / <filename>
url = fmt.Sprintf("%s/%s/%s/%s/%s/%s", pd.CdnUrl, md.Name, fileName, hashType, hash, fileName)
// We need to figure out the hashtype for templating purposes:
hashType := "sha512"
switch len(hash) {
case 128:
hashType = "sha512"
case 64:
hashType = "sha256"
case 40:
hashType = "sha1"
case 32:
hashType = "md5"
}

pd.Log.Printf("downloading %s", url)
// need the name of the file without "SOURCES/":
fileName := strings.Split(path, "/")[1]

req, err := http.NewRequest("GET", url, nil)
if err != nil {
return fmt.Errorf("could not create new http request: %v", err)
// Feed our template info to ProcessUrl and transform to the real values: ( {{.Name}}, {{.Branch}}, {{.Hash}}, {{.Hashtype}}, {{.Filename}} )
url, hasTemplate := ProcessUrl(pd.CdnUrl, md.Name, branchName, hash, hashType, fileName)

var req *http.Request
var resp *http.Response

// Download the --cdn-url given, but *only* if it contains template strings ( {{.Name}} , {{.Hash}} , etc. )
// Otherwise we need to fall back to the traditional cdn-url patterns
if hasTemplate {
pd.Log.Printf("downloading %s", url)

req, err := http.NewRequest("GET", url, nil)
if err != nil {
return fmt.Errorf("could not create new http request: %v", err)
}
req.Header.Set("Accept-Encoding", "*")

resp, err = client.Do(req)
if err != nil {
return fmt.Errorf("could not download dist-git file: %v", err)
}
}
req.Header.Set("Accept-Encoding", "*")

resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("could not download dist-git file: %v", err)
// Default cdn-url: If we don't have a templated download string, try the default <SITE>/<PKG>/<BRANCH>/<HASH> pattern:
if resp == nil || resp.StatusCode != http.StatusOK {
url = fmt.Sprintf("%s/%s/%s/%s", pd.CdnUrl, md.Name, branchName, hash)
pd.Log.Printf("Attempting default URL: %s", url)
req, err = http.NewRequest("GET", url, nil)
if err != nil {
return fmt.Errorf("could not create new http request: %v", err)
}
req.Header.Set("Accept-Encoding", "*")
resp, err = client.Do(req)
if err != nil {
return fmt.Errorf("could not download dist-git file: %v", err)
}
}
if resp.StatusCode != http.StatusOK {

// If the default URL fails, we have one more pattern to try. The simple <SITE>/<HASH> pattern
// If this one fails, we are truly lost, and have to bail out w/ an error:
if resp == nil || resp.StatusCode != http.StatusOK {
url = fmt.Sprintf("%s/%s", pd.CdnUrl, hash)
pd.Log.Printf("Attempting 2nd fallback URL: %s", url)
req, err = http.NewRequest("GET", url, nil)
if err != nil {
return fmt.Errorf("could not create new http request: %v", err)
Expand Down Expand Up @@ -458,3 +492,24 @@ func (g *GitMode) ImportName(pd *data.ProcessData, md *data.ModeData) string {

return strings.Replace(strings.TrimPrefix(md.TagBranch, "refs/heads/"), "%", "_", -1)
}

// Given a cdnUrl string as input, return same string, but with substituted
// template values ( {{.Name}} , {{.Hash}}, {{.Filename}}, etc. )
func ProcessUrl(cdnUrl string, name string, branch string, hash string, hashtype string, filename string) (string, bool) {
tmpUrl := Lookaside{name, branch, hash, hashtype, filename}

// If we run into trouble with our template parsing, we'll just return the cdnUrl, exactly as we found it
tmpl, err := template.New("").Parse(cdnUrl)
if err != nil {
return cdnUrl, false
}

var result bytes.Buffer
err = tmpl.Execute(&result, tmpUrl)
if err != nil {
log.Fatalf("ERROR: Could not process CDN URL template(s) from URL string: %s\n", cdnUrl)
}

return result.String(), true

}
Loading

0 comments on commit 2c6d6f0

Please sign in to comment.