Skip to content

Commit

Permalink
Merge pull request #1762 from SaschaSchwarze0/sascha-test-connection
Browse files Browse the repository at this point in the history
Test connectivity to the Git or Registry host before accessing the source
  • Loading branch information
openshift-merge-bot[bot] authored Jan 7, 2025
2 parents 4d626db + 48b2ed6 commit bcd4ca2
Show file tree
Hide file tree
Showing 9 changed files with 249 additions and 0 deletions.
7 changes: 7 additions & 0 deletions cmd/bundle/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,13 @@ func Do(ctx context.Context) error {
return fmt.Errorf("mandatory flag --image is not set")
}

// check the endpoint, if hostname extraction fails, ignore that failure
if hostname, port, err := image.ExtractHostnamePort(flagValues.image); err == nil {
if !util.TestConnection(hostname, port, 9) {
log.Printf("Warning: a connection test to %s:%d failed. The operation will likely fail.\n", hostname, port)
}
}

ref, err := name.ParseReference(flagValues.image)
if err != nil {
return err
Expand Down
7 changes: 7 additions & 0 deletions cmd/git/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,13 @@ func runGitClone(ctx context.Context) error {
return &ExitError{Code: 101, Message: "the 'target' argument must not be empty"}
}

// check the endpoint, if hostname extraction fails, ignore that failure
if hostname, port, err := shpgit.ExtractHostnamePort(flagValues.url); err == nil {
if !util.TestConnection(hostname, port, 9) {
log.Printf("Warning: a connection test to %s:%d failed. The operation will likely fail.\n", hostname, port)
}
}

if err := clone(ctx); err != nil {
return err
}
Expand Down
26 changes: 26 additions & 0 deletions pkg/git/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,32 @@ const (
gitProtocol = "ssh"
)

// ExtractHostnamePort extracts the hostname and port of the provided Git URL
func ExtractHostnamePort(url string) (string, int, error) {
endpoint, err := transport.NewEndpoint(url)
if err != nil {
return "", 0, err
}

port := endpoint.Port

if port == 0 {
switch endpoint.Protocol {
case httpProtocol:
port = 80
case httpsProtocol:
port = 443
case gitProtocol:
port = 22

default:
return "", 0, fmt.Errorf("unknown protocol: %s", endpoint.Protocol)
}
}

return endpoint.Host, port, nil
}

// ValidateGitURLExists validate if a source URL exists or not
// Note: We have an upcoming PR for the Build Status, where we
// intend to define a single Status.Reason in the form of 'remoteRepositoryUnreachable',
Expand Down
21 changes: 21 additions & 0 deletions pkg/git/git_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,27 @@ import (

var _ = Describe("Git", func() {

DescribeTable("the extraction of hostname and port",
func(url string, expectedHost string, expectedPort int, expectError bool) {
host, port, err := git.ExtractHostnamePort(url)
if expectError {
Expect(err).To(HaveOccurred())
} else {
Expect(err).ToNot(HaveOccurred())
Expect(host).To(Equal(expectedHost), "for "+url)
Expect(port).To(Equal(expectedPort), "for "+url)
}
},
Entry("Check heritage SSH URL with default port", "ssh://github.com/shipwright-io/build.git", "github.com", 22, false),
Entry("Check heritage SSH URL with custom port", "ssh://github.com:12134/shipwright-io/build.git", "github.com", 12134, false),
Entry("Check SSH URL with default port", "git@github.com:shipwright-io/build.git", "github.com", 22, false),
Entry("Check HTTP URL with default port", "http://github.com/shipwright-io/build.git", "github.com", 80, false),
Entry("Check HTTPS URL with default port", "https://github.com/shipwright-io/build.git", "github.com", 443, false),
Entry("Check HTTPS URL with custom port", "https://github.com:9443/shipwright-io/build.git", "github.com", 9443, false),
Entry("Check HTTPS URL with credentials", "https://somebody:password@github.com/shipwright-io/build.git", "github.com", 443, false),
Entry("Check invalid URL", "ftp://github.com/shipwright-io/build", "", 0, true),
)

DescribeTable("the source url validation errors",
func(url string, expected types.GomegaMatcher) {
Expect(git.ValidateGitURLExists(context.TODO(), url)).To(expected)
Expand Down
48 changes: 48 additions & 0 deletions pkg/image/endpoint.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright The Shipwright Contributors
//
// SPDX-License-Identifier: Apache-2.0

package image

import (
"fmt"
"strconv"
"strings"

"github.com/google/go-containerregistry/pkg/name"
)

// ExtractHostnamePort tries to extract the hostname and port of the provided image URL
func ExtractHostnamePort(url string) (string, int, error) {
ref, err := name.ParseReference(url)
if err != nil {
return "", 0, err
}

registry := ref.Context().Registry
host := registry.RegistryStr()
hostname := host
port := 0

parts := strings.SplitN(host, ":", 2)
if len(parts) == 2 {
hostname = parts[0]
if port, err = strconv.Atoi(parts[1]); err != nil {
return "", 0, err
}
} else {
scheme := registry.Scheme()

switch scheme {
case "http":
port = 80
case "https":
port = 443

default:
return "", 0, fmt.Errorf("unknown protocol: %s", scheme)
}
}

return hostname, port, nil
}
32 changes: 32 additions & 0 deletions pkg/image/endpoint_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright The Shipwright Contributors
//
// SPDX-License-Identifier: Apache-2.0

package image_test

import (
"github.com/shipwright-io/build/pkg/image"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

var _ = Describe("Endpoints", func() {

DescribeTable("the extraction of hostname and port",
func(url string, expectedHost string, expectedPort int, expectError bool) {
host, port, err := image.ExtractHostnamePort(url)
if expectError {
Expect(err).To(HaveOccurred())
} else {
Expect(err).ToNot(HaveOccurred())
Expect(host).To(Equal(expectedHost), "for "+url)
Expect(port).To(Equal(expectedPort), "for "+url)
}
},
Entry("Check a URL with default port", "registry.access.redhat.com/ubi9/ubi-minimal", "registry.access.redhat.com", 443, false),
Entry("Check a URL with custom port", "registry.access.redhat.com:9443/ubi9/ubi-minimal", "registry.access.redhat.com", 9443, false),
Entry("Check a URL without host", "ubuntu", "index.docker.io", 443, false),
Entry("Check invalid URL", "ftp://registry.access.redhat.com/ubi9/ubi-minimal", "", 0, true),
)
})
30 changes: 30 additions & 0 deletions pkg/util/tcp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright The Shipwright Contributors
//
// SPDX-License-Identifier: Apache-2.0

package util

import (
"fmt"
"net"
"time"
)

// TestConnection tries to establish a connection to a provided host using a 5 seconds timeout.
func TestConnection(hostname string, port int, retries int) bool {
host := fmt.Sprintf("%s:%d", hostname, port)

dialer := &net.Dialer{
Timeout: 5 * time.Second,
}

for i := 0; i <= retries; i++ {
conn, _ := dialer.Dial("tcp", host)
if conn != nil {
_ = conn.Close()
return true
}
}

return false
}
61 changes: 61 additions & 0 deletions pkg/util/tcp_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright The Shipwright Contributors
//
// SPDX-License-Identifier: Apache-2.0

package util_test

import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/shipwright-io/build/pkg/util"
)

var _ = Describe("TCP", func() {

Context("TestConnection", func() {

var result bool
var hostname string
var port int

JustBeforeEach(func() {
result = util.TestConnection(hostname, port, 1)
})

Context("For a broken endpoint", func() {

BeforeEach(func() {
hostname = "shipwright.io"
port = 33333
})

It("returns false", func() {
Expect(result).To(BeFalse())
})
})

Context("For an unknown host", func() {

BeforeEach(func() {
hostname = "shipwright-dhasldglidgewidgwd.io"
port = 33333
})

It("returns false", func() {
Expect(result).To(BeFalse())
})
})

Context("For a functional endpoint", func() {

BeforeEach(func() {
hostname = "github.com"
port = 443
})

It("returns true", func() {
Expect(result).To(BeTrue())
})
})
})
})
17 changes: 17 additions & 0 deletions pkg/util/util_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright The Shipwright Contributors
//
// SPDX-License-Identifier: Apache-2.0

package util_test

import (
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

func TestGit(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Util Suite")
}

0 comments on commit bcd4ca2

Please sign in to comment.