debian-forge-composer/internal/ostree/ostree.go
Brian C. Lane 7a4bb863dd Update deprecated io/ioutil functions
ioutil has been deprecated since go 1.16, this fixes all of the
deprecated functions we are using:

ioutil.ReadFile -> os.ReadFile
ioutil.ReadAll -> io.ReadAll
ioutil.WriteFile -> os.WriteFile
ioutil.TempFile -> os.CreateTemp
ioutil.TempDir -> os.MkdirTemp

All of the above are a simple name change, the function arguments and
results are exactly the same as before.

ioutil.ReadDir -> os.ReadDir

now returns a os.DirEntry but the IsDir and Name functions work the
same. The difference is that the FileInfo must be retrieved with the
Info() function which can also return an error.

These were identified by running:
golangci-lint run --build-tags=integration ./...
2023-03-07 09:22:23 -08:00

182 lines
5.2 KiB
Go

package ostree
import (
"crypto/tls"
"crypto/x509"
"encoding/hex"
"fmt"
"io"
"net/http"
"net/url"
"os"
"path"
"regexp"
"strings"
"time"
"github.com/osbuild/osbuild-composer/internal/rhsm"
)
var ostreeRefRE = regexp.MustCompile(`^(?:[\w\d][-._\w\d]*\/)*[\w\d][-._\w\d]*$`)
// RequestParams serves as input for ResolveParams, and contains all necessary variables to resolve
// a ref, which can then be turned into a CommitSpec.
type RequestParams struct {
URL string `json:"url"`
Ref string `json:"ref"`
Parent string `json:"parent"`
RHSM bool `json:"rhsm"`
}
// CommitSpec specifies an ostree commit using any combination of Ref (branch), URL (source), and Checksum (commit ID).
type CommitSpec struct {
// Ref for the commit. Can be empty.
Ref string
// URL of the repo where the commit can be fetched, if available.
URL string
ContentURL string
Secrets string
// Checksum of the commit.
Checksum string
}
// Remote defines the options that can be set for an OSTree Remote configuration.
type Remote struct {
Name string
URL string
ContentURL string
GPGKeyPaths []string
}
func VerifyRef(ref string) bool {
return len(ref) > 0 && ostreeRefRE.MatchString(ref)
}
// ResolveRef resolves the URL path specified by the location and ref
// (location+"refs/heads/"+ref) and returns the commit ID for the named ref. If
// there is an error, it will be of type ResolveRefError.
func ResolveRef(location, ref string, consumerCerts bool, subs *rhsm.Subscriptions, ca *string) (string, error) {
u, err := url.Parse(location)
if err != nil {
return "", NewResolveRefError(fmt.Sprintf("error parsing ostree repository location: %v", err))
}
u.Path = path.Join(u.Path, "refs/heads/", ref)
var client *http.Client
if consumerCerts {
if subs == nil {
subs, err = rhsm.LoadSystemSubscriptions()
if subs.Consumer == nil || err != nil {
return "", NewResolveRefError("error adding rhsm certificates when resolving ref")
}
}
tlsConf := &tls.Config{
MinVersion: tls.VersionTLS12,
}
if ca != nil {
caCertPEM, err := os.ReadFile(*ca)
if err != nil {
return "", NewResolveRefError("error adding rhsm certificates when resolving ref")
}
roots := x509.NewCertPool()
ok := roots.AppendCertsFromPEM(caCertPEM)
if !ok {
return "", NewResolveRefError("error adding rhsm certificates when resolving ref")
}
tlsConf.RootCAs = roots
}
cert, err := tls.LoadX509KeyPair(subs.Consumer.ConsumerCert, subs.Consumer.ConsumerKey)
if err != nil {
return "", NewResolveRefError("error adding rhsm certificates when resolving ref")
}
tlsConf.Certificates = []tls.Certificate{cert}
client = &http.Client{
Transport: &http.Transport{
TLSClientConfig: tlsConf,
},
Timeout: 300 * time.Second,
}
} else {
client = &http.Client{}
}
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
if err != nil {
return "", NewResolveRefError("error adding rhsm certificates when resolving ref")
}
resp, err := client.Do(req)
if err != nil {
return "", NewResolveRefError(fmt.Sprintf("error sending request to ostree repository %q: %v", u.String(), err))
}
if resp.StatusCode != http.StatusOK {
return "", NewResolveRefError("ostree repository %q returned status: %s", u.String(), resp.Status)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", NewResolveRefError(fmt.Sprintf("error reading response from ostree repository %q: %v", u.String(), err))
}
parent := strings.TrimSpace(string(body))
// Check that this is at least a hex string.
_, err = hex.DecodeString(parent)
if err != nil {
return "", NewResolveRefError("ostree repository %q returned invalid reference", u.String())
}
return parent, nil
}
// ResolveParams resolves the ostree request parameters into the necessary ref
// for the image build pipeline and a commit (checksum) to be fetched.
//
// If a URL is defined in the RequestParams, the checksum of the Parent ref is
// resolved, otherwise the checksum is an empty string. Specifying Parent
// without URL results in a ParameterComboError. Failure to resolve the
// checksum results in a ResolveRefError.
//
// If Parent is not specified in the RequestParams, the value of Ref is used.
//
// If any ref (Ref or Parent) is malformed, the function returns with a RefError.
func ResolveParams(params RequestParams) (ref, checksum string, err error) {
ref = params.Ref
// Determine value of ref
if !VerifyRef(params.Ref) {
return "", "", NewRefError("Invalid ostree ref %q", params.Ref)
}
// Determine value of parentRef
parentRef := params.Parent
if parentRef != "" {
// verify format of parent ref
if !VerifyRef(params.Parent) {
return "", "", NewRefError("Invalid ostree parent ref %q", params.Parent)
}
if params.URL == "" {
// specifying parent ref also requires URL
return "", "", NewParameterComboError("ostree parent ref specified, but no URL to retrieve it")
}
} else {
// if parent is not provided, use ref
parentRef = params.Ref
}
// Resolve parent checksum
if params.URL != "" {
// If a URL is specified, we need to fetch the commit at the URL.
parent, err := ResolveRef(params.URL, parentRef, params.RHSM, nil, nil)
if err != nil {
return "", "", err // ResolveRefError
}
checksum = parent
}
return
}