2021-12-29 10:14:56 -05:00
|
|
|
package common
|
|
|
|
|
2022-12-22 13:19:42 -05:00
|
|
|
import (
|
2023-11-24 03:40:10 -05:00
|
|
|
"context"
|
2022-12-22 13:19:42 -05:00
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"io/fs"
|
2024-05-20 11:05:21 -05:00
|
|
|
"net"
|
2022-12-22 13:19:42 -05:00
|
|
|
"os"
|
2023-10-12 20:45:20 -05:00
|
|
|
"regexp"
|
2023-07-31 14:16:09 -05:00
|
|
|
"strings"
|
2022-12-22 13:19:42 -05:00
|
|
|
"syscall"
|
|
|
|
"time"
|
|
|
|
"unicode/utf8"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
httpTimeout = 5 * time.Minute
|
|
|
|
certsPath = "/etc/containers/certs.d"
|
|
|
|
homeCertsDir = ".config/containers/certs.d"
|
2024-05-06 15:43:41 -05:00
|
|
|
ClientCertFilename = "client.cert"
|
|
|
|
ClientKeyFilename = "client.key"
|
|
|
|
CaCertFilename = "ca.crt"
|
2023-08-19 00:52:03 -05:00
|
|
|
|
|
|
|
CosignSignature = "cosign"
|
|
|
|
CosignSigKey = "dev.cosignproject.cosign/signature"
|
|
|
|
NotationSignature = "notation"
|
2023-09-06 11:58:00 -05:00
|
|
|
// same value as github.com/notaryproject/notation-go/registry.ArtifactTypeNotation (assert by internal test).
|
|
|
|
// reason used: to reduce zot minimal binary size (otherwise adds oras.land/oras-go/v2 deps).
|
|
|
|
ArtifactTypeNotation = "application/vnd.cncf.notary.signature"
|
2023-11-06 17:09:39 -05:00
|
|
|
ArtifactTypeCosign = "application/vnd.dev.cosign.artifact.sig.v1+json"
|
2022-12-22 13:19:42 -05:00
|
|
|
)
|
|
|
|
|
2023-10-30 15:06:04 -05:00
|
|
|
var cosignTagRule = regexp.MustCompile(`sha256\-.+\.sig`)
|
|
|
|
|
|
|
|
func IsCosignTag(tag string) bool {
|
|
|
|
return cosignTagRule.MatchString(tag)
|
|
|
|
}
|
|
|
|
|
2023-06-16 12:27:33 -05:00
|
|
|
func Contains[T comparable](elems []T, v T) bool {
|
|
|
|
for _, s := range elems {
|
|
|
|
if v == s {
|
2021-12-29 10:14:56 -05:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
2022-12-22 13:19:42 -05:00
|
|
|
|
2023-04-24 13:13:15 -05:00
|
|
|
// first match of item in [].
|
|
|
|
func Index(slice []string, item string) int {
|
|
|
|
for k, v := range slice {
|
|
|
|
if item == v {
|
|
|
|
return k
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
|
|
|
|
// remove matches of item in [].
|
|
|
|
func RemoveFrom(inputSlice []string, item string) []string {
|
|
|
|
var newSlice []string
|
|
|
|
|
|
|
|
for _, v := range inputSlice {
|
|
|
|
if item != v {
|
|
|
|
newSlice = append(newSlice, v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return newSlice
|
|
|
|
}
|
|
|
|
|
2022-12-22 13:19:42 -05:00
|
|
|
func TypeOf(v interface{}) string {
|
|
|
|
return fmt.Sprintf("%T", v)
|
|
|
|
}
|
|
|
|
|
|
|
|
func DirExists(d string) bool {
|
|
|
|
if !utf8.ValidString(d) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
fileInfo, err := os.Stat(d)
|
|
|
|
if err != nil {
|
|
|
|
if e, ok := err.(*fs.PathError); ok && errors.Is(e.Err, syscall.ENAMETOOLONG) || //nolint: errorlint
|
|
|
|
errors.Is(e.Err, syscall.EINVAL) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil && os.IsNotExist(err) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if !fileInfo.IsDir() {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
2023-03-09 13:43:26 -05:00
|
|
|
|
|
|
|
// Used to filter a json fields by using an intermediate struct.
|
|
|
|
func MarshalThroughStruct(obj interface{}, throughStruct interface{}) ([]byte, error) {
|
|
|
|
toJSON, err := json.Marshal(obj)
|
|
|
|
if err != nil {
|
|
|
|
return []byte{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = json.Unmarshal(toJSON, throughStruct)
|
|
|
|
if err != nil {
|
|
|
|
return []byte{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
toJSON, err = json.Marshal(throughStruct)
|
|
|
|
if err != nil {
|
|
|
|
return []byte{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return toJSON, nil
|
|
|
|
}
|
2023-07-31 14:16:09 -05:00
|
|
|
|
|
|
|
func ContainsStringIgnoreCase(strSlice []string, str string) bool {
|
|
|
|
for _, val := range strSlice {
|
|
|
|
if strings.EqualFold(val, str) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
2023-10-12 20:45:20 -05:00
|
|
|
|
|
|
|
// this function will check if tag is a referrers tag
|
|
|
|
// (https://github.com/opencontainers/distribution-spec/blob/main/spec.md#referrers-tag-schema).
|
|
|
|
func IsReferrersTag(tag string) bool {
|
|
|
|
referrersTagRule := regexp.MustCompile(`sha256\-[A-Za-z0-9]*$`)
|
|
|
|
|
|
|
|
return referrersTagRule.MatchString(tag)
|
|
|
|
}
|
2023-11-24 03:40:10 -05:00
|
|
|
|
|
|
|
func IsContextDone(ctx context.Context) bool {
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return true
|
|
|
|
default:
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
2024-05-20 11:05:21 -05:00
|
|
|
|
|
|
|
// get a list of IP addresses configured on the host's
|
|
|
|
// interfaces.
|
|
|
|
func GetLocalIPs() ([]string, error) {
|
|
|
|
var localIPs []string
|
|
|
|
|
|
|
|
ifaces, err := net.Interfaces()
|
|
|
|
if err != nil {
|
|
|
|
return []string{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, i := range ifaces {
|
|
|
|
addrs, err := i.Addrs()
|
|
|
|
if err != nil {
|
|
|
|
return localIPs, err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, addr := range addrs {
|
|
|
|
if localIP, ok := addr.(*net.IPNet); ok {
|
|
|
|
localIPs = append(localIPs, localIP.IP.String())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return localIPs, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// get a list of listening sockets on the host (IP:port).
|
|
|
|
// IPv6 is returned as [host]:port.
|
|
|
|
func GetLocalSockets(port string) ([]string, error) {
|
|
|
|
localIPs, err := GetLocalIPs()
|
|
|
|
if err != nil {
|
|
|
|
return []string{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
localSockets := make([]string, len(localIPs))
|
|
|
|
|
|
|
|
for idx, ip := range localIPs {
|
|
|
|
// JoinHostPort automatically wraps IPv6 addresses in []
|
|
|
|
localSockets[idx] = net.JoinHostPort(ip, port)
|
|
|
|
}
|
|
|
|
|
|
|
|
return localSockets, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetIPFromHostName(host string) ([]string, error) {
|
|
|
|
addrs, err := net.LookupIP(host)
|
|
|
|
if err != nil {
|
|
|
|
return []string{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
ips := make([]string, 0, len(addrs))
|
|
|
|
|
|
|
|
for _, ip := range addrs {
|
|
|
|
ips = append(ips, ip.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
return ips, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// checks if 2 sockets are equal at the host port level.
|
|
|
|
func AreSocketsEqual(socketA string, socketB string) (bool, error) {
|
|
|
|
hostA, portA, err := net.SplitHostPort(socketA)
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
hostB, portB, err := net.SplitHostPort(socketB)
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
hostAIP := net.ParseIP(hostA)
|
|
|
|
if hostAIP == nil {
|
|
|
|
// this could be a fully-qualified domain name (FQDN)
|
|
|
|
// for FQDN, just a normal compare is enough
|
|
|
|
return hostA == hostB, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
hostBIP := net.ParseIP(hostB)
|
|
|
|
if hostBIP == nil {
|
|
|
|
// if the host part of socketA was parsed successfully, it was an IP
|
|
|
|
// if the host part of socketA was an FQDN, then the comparison is
|
|
|
|
// already done as the host of socketB is also assumed to be an FQDN.
|
|
|
|
// since the parsing failed, assume that A and B are not equal.
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return (hostAIP.Equal(hostBIP) && (portA == portB)), nil
|
|
|
|
}
|