github.com/olli-ai/jx/v2@v2.0.400-0.20210921045218-14731b4dd448/pkg/cmd/controller/pipeline/pipelinerunner_controller_test.go (about)

     1  // +build unit
     2  
     3  package pipeline
     4  
     5  import (
     6  	"bytes"
     7  	"context"
     8  	"errors"
     9  	"fmt"
    10  	"io/ioutil"
    11  	"net"
    12  	"net/http"
    13  	"sync"
    14  	"testing"
    15  	"time"
    16  
    17  	v1 "github.com/jenkins-x/jx-api/pkg/apis/jenkins.io/v1"
    18  	"github.com/jenkins-x/jx-api/pkg/client/clientset/versioned/fake"
    19  	"github.com/jenkins-x/jx-logging/pkg/log"
    20  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    21  	"k8s.io/apimachinery/pkg/runtime"
    22  
    23  	. "github.com/onsi/ginkgo"
    24  	. "github.com/onsi/gomega"
    25  )
    26  
    27  const (
    28  	testRequestWithoutProwJobName = `
    29  {
    30    "labels": {
    31      "created-by-prow": "true"
    32    },
    33    "prowJobSpec": {
    34      "type": "pullrequest",
    35      "agent": "tekton",
    36      "cluster": "default",
    37      "namespace": "jx",
    38      "job": "serverless-jenkins",
    39      "refs": {
    40        "org": "jenkins-x-quickstarts",
    41        "repo": "golang-http",
    42        "repo_link": "https://github.com/jenkins-x-quickstarts/golang-http",
    43        "base_ref": "master",
    44        "base_sha": "3f00363d651280ab2a8ee67f395de1689156d762",
    45        "pulls": [
    46          {
    47            "number": 1,
    48            "sha": "06b5fa6804aa0bd1f4f533010d1b335918a433e2"
    49          }
    50        ]
    51      },
    52      "report": true,
    53      "context": "serverless-jenkins",
    54      "rerun_command": "/test this"
    55    }
    56  }
    57  `
    58  	testRequestMissingPullRefs = `
    59  {
    60    "labels": {
    61      "created-by-prow": "true",
    62      "prowJobName": "cdf89f04-98ec-11e9-a846-4ad95a1bb3ab"
    63    },
    64    "prowJobSpec": {
    65      "type": "pullrequest",
    66      "agent": "tekton",
    67      "cluster": "default",
    68      "namespace": "jx",
    69      "job": "serverless-jenkins",
    70      "report": true,
    71      "context": "serverless-jenkins",
    72      "rerun_command": "/test this"
    73    }
    74  }
    75  `
    76  )
    77  
    78  func TestPipelineRunner(t *testing.T) {
    79  	RegisterFailHandler(Fail)
    80  	RunSpecs(t, "Pipeline Runner Test Suite")
    81  }
    82  
    83  var _ = Describe("Pipeline Runner", func() {
    84  	BeforeSuite(func() {
    85  		log.SetOutput(GinkgoWriter)
    86  	})
    87  
    88  	Describe("when running", func() {
    89  		var (
    90  			client *http.Client
    91  			host   string
    92  			port   int
    93  			err    error
    94  			ctx    context.Context
    95  			cancel context.CancelFunc
    96  		)
    97  
    98  		BeforeEach(func() {
    99  			client = &http.Client{}
   100  			Expect(err).Should(BeNil())
   101  
   102  			jxClient := fake.NewSimpleClientset()
   103  			Expect(err).Should(BeNil())
   104  
   105  			host = "127.0.0.1"
   106  			port, _ = getFreePort()
   107  			controller := controller{
   108  				path:            "/",
   109  				bindAddress:     host,
   110  				port:            port,
   111  				useMetaPipeline: true,
   112  				jxClient:        jxClient,
   113  				ns:              "jx",
   114  			}
   115  
   116  			go func() {
   117  				var wg sync.WaitGroup
   118  				ctx, cancel = context.WithCancel(context.Background())
   119  				controller.startWorkers(ctx, &wg, cancel)
   120  				wg.Wait()
   121  			}()
   122  
   123  			err := waitForOpenPort(host, port, time.Duration(5)*time.Second)
   124  			Expect(err).Should(BeNil())
   125  		})
   126  
   127  		AfterEach(func() {
   128  			cancel()
   129  		})
   130  
   131  		It("GET requests return with HTTP 200 and request POST", func() {
   132  			resp, err := client.Get(fmt.Sprintf("http://localhost:%d/", port))
   133  			Expect(err).Should(BeNil())
   134  			Expect(resp.StatusCode).Should(Equal(200))
   135  
   136  			defer func() {
   137  				err := resp.Body.Close()
   138  				Expect(err).Should(BeNil())
   139  			}()
   140  
   141  			htmlData, err := ioutil.ReadAll(resp.Body)
   142  			Expect(string(htmlData)).Should(ContainSubstring("please POST JSON "))
   143  		})
   144  
   145  		It("/health returns HTTP 204", func() {
   146  			resp, err := client.Get(fmt.Sprintf("http://localhost:%d%s", port, healthPath))
   147  			Expect(err).Should(BeNil())
   148  			Expect(resp.StatusCode).Should(Equal(http.StatusNoContent))
   149  		})
   150  
   151  		It("/ready returns HTTP 204", func() {
   152  			resp, err := client.Get(fmt.Sprintf("http://localhost:%d%s", port, readyPath))
   153  			Expect(err).Should(BeNil())
   154  			Expect(resp.StatusCode).Should(Equal(http.StatusNoContent))
   155  		})
   156  
   157  		It("POST returns HTTP 400 for invalid JSON", func() {
   158  			var json = []byte("{\"foo\":\"bar\"}")
   159  			resp, err := client.Post(fmt.Sprintf("http://localhost:%d/", port), "application/json", bytes.NewBuffer(json))
   160  			Expect(err).Should(BeNil())
   161  			Expect(resp.StatusCode).Should(Equal(http.StatusBadRequest))
   162  
   163  			defer func() {
   164  				err := resp.Body.Close()
   165  				Expect(err).Should(BeNil())
   166  			}()
   167  
   168  			htmlData, err := ioutil.ReadAll(resp.Body)
   169  			Expect(string(htmlData)).Should(ContainSubstring("could not start pipeline"))
   170  		})
   171  
   172  		It("POST returns HTTP 400 for missing pull refs", func() {
   173  			resp, err := client.Post(fmt.Sprintf("http://localhost:%d/", port), "application/json", bytes.NewBuffer([]byte(testRequestMissingPullRefs)))
   174  			Expect(err).Should(BeNil())
   175  			Expect(resp.StatusCode).Should(Equal(http.StatusBadRequest))
   176  
   177  			defer func() {
   178  				err := resp.Body.Close()
   179  				Expect(err).Should(BeNil())
   180  			}()
   181  
   182  			htmlData, err := ioutil.ReadAll(resp.Body)
   183  			Expect(string(htmlData)).Should(ContainSubstring("no prowJobSpec.refs passed"))
   184  		})
   185  
   186  		It("POST returns HTTP 400 for missing prow job name", func() {
   187  			resp, err := client.Post(fmt.Sprintf("http://localhost:%d/", port), "application/json", bytes.NewBuffer([]byte(testRequestWithoutProwJobName)))
   188  			Expect(err).Should(BeNil())
   189  			Expect(resp.StatusCode).Should(Equal(http.StatusBadRequest))
   190  
   191  			defer func() {
   192  				err := resp.Body.Close()
   193  				Expect(err).Should(BeNil())
   194  			}()
   195  
   196  			htmlData, err := ioutil.ReadAll(resp.Body)
   197  			Expect(string(htmlData)).Should(ContainSubstring("unable to find prow job name in pipeline request"))
   198  		})
   199  	})
   200  
   201  	Describe("#getSourceURL", func() {
   202  		var (
   203  			testController controller
   204  			expectedURL    = "http://github.com/jenkins-x/jx.git"
   205  		)
   206  
   207  		BeforeEach(func() {
   208  			var jxObjects []runtime.Object
   209  
   210  			sourceRepo := &v1.SourceRepository{
   211  				ObjectMeta: metav1.ObjectMeta{
   212  					Namespace: "jx",
   213  					Labels:    map[string]string{"owner": "jenkins-x", "repository": "jx"},
   214  				},
   215  
   216  				Spec: v1.SourceRepositorySpec{
   217  					Provider: "http://github.com",
   218  				},
   219  			}
   220  
   221  			jxObjects = append(jxObjects, sourceRepo)
   222  			jxClient := fake.NewSimpleClientset(jxObjects...)
   223  
   224  			testController = controller{
   225  				jxClient: jxClient,
   226  				ns:       "jx",
   227  			}
   228  
   229  		})
   230  
   231  		It("retrieves source URL from cluster", func() {
   232  			url := testController.getSourceURL("jenkins-x", "jx")
   233  			Expect(url).Should(Equal(expectedURL))
   234  		})
   235  
   236  		It("returns the empty string for an unknown repo", func() {
   237  			url := testController.getSourceURL("jenkins-x", "foo")
   238  			Expect(url).Should(BeEmpty())
   239  		})
   240  	})
   241  })
   242  
   243  // getFreePort asks the kernel for a free open port that is ready to use.
   244  func getFreePort() (int, error) {
   245  	addr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:0")
   246  	if err != nil {
   247  		return 0, err
   248  	}
   249  
   250  	l, err := net.ListenTCP("tcp", addr)
   251  	if err != nil {
   252  		return 0, err
   253  	}
   254  	defer func() {
   255  		_ = l.Close()
   256  	}()
   257  	return l.Addr().(*net.TCPAddr).Port, nil
   258  }
   259  
   260  func waitForOpenPort(host string, port int, timeOut time.Duration) error {
   261  	connectChannel := make(chan error, 1)
   262  	go func() {
   263  		for {
   264  			addr := fmt.Sprintf("%s:%d", host, port)
   265  			conn, err := net.Dial("tcp", addr)
   266  			if err != nil {
   267  				continue
   268  			}
   269  
   270  			err = conn.Close()
   271  			if err != nil {
   272  				connectChannel <- err
   273  			}
   274  			connectChannel <- nil
   275  		}
   276  	}()
   277  
   278  	select {
   279  	case err := <-connectChannel:
   280  		return err
   281  	case <-time.After(timeOut):
   282  		return errors.New("timout waiting for open port")
   283  	}
   284  }