181 lines
5.9 KiB
Go
181 lines
5.9 KiB
Go
//go:build integration
|
|
// +build integration
|
|
|
|
// Copyright 2025 Cloudbase Solutions SRL
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
// not use this file except in compliance with the License. You may obtain
|
|
// a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
// License for the specific language governing permissions and limitations
|
|
// under the License.
|
|
package integration
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/google/go-github/v71/github"
|
|
|
|
commonParams "github.com/cloudbase/garm-provider-common/params"
|
|
"github.com/cloudbase/garm/params"
|
|
)
|
|
|
|
func (suite *GarmSuite) TestWorkflowJobs() {
|
|
suite.TriggerWorkflow(suite.ghToken, orgName, repoName, workflowFileName, "org-runner")
|
|
suite.ValidateJobLifecycle("org-runner")
|
|
|
|
suite.TriggerWorkflow(suite.ghToken, orgName, repoName, workflowFileName, "repo-runner")
|
|
suite.ValidateJobLifecycle("repo-runner")
|
|
}
|
|
|
|
func (suite *GarmSuite) TriggerWorkflow(ghToken, orgName, repoName, workflowFileName, labelName string) {
|
|
t := suite.T()
|
|
t.Logf("Trigger workflow with label %s", labelName)
|
|
|
|
client := getGithubClient(ghToken)
|
|
eventReq := github.CreateWorkflowDispatchEventRequest{
|
|
Ref: "main",
|
|
Inputs: map[string]interface{}{
|
|
"sleep_time": "50",
|
|
"runner_label": labelName,
|
|
},
|
|
}
|
|
_, err := client.Actions.CreateWorkflowDispatchEventByFileName(context.Background(), orgName, repoName, workflowFileName, eventReq)
|
|
suite.NoError(err, "error triggering workflow")
|
|
}
|
|
|
|
func (suite *GarmSuite) ValidateJobLifecycle(label string) {
|
|
t := suite.T()
|
|
t.Logf("Validate GARM job lifecycle with label %s", label)
|
|
|
|
// wait for job list to be updated
|
|
job, err := suite.waitLabelledJob(label, 4*time.Minute)
|
|
suite.NoError(err, "error waiting for job to be created")
|
|
|
|
// check expected job status
|
|
job, err = suite.waitJobStatus(job.ID, params.JobStatusQueued, 4*time.Minute)
|
|
suite.NoError(err, "error waiting for job to be queued")
|
|
|
|
job, err = suite.waitJobStatus(job.ID, params.JobStatusInProgress, 4*time.Minute)
|
|
suite.NoError(err, "error waiting for job to be in progress")
|
|
|
|
// check expected instance status
|
|
instance, err := suite.waitInstanceStatus(job.RunnerName, commonParams.InstanceRunning, params.RunnerActive, 5*time.Minute)
|
|
suite.NoError(err, "error waiting for instance to be running")
|
|
|
|
// wait for job to be completed
|
|
_, err = suite.waitJobStatus(job.ID, params.JobStatusCompleted, 4*time.Minute)
|
|
suite.NoError(err, "error waiting for job to be completed")
|
|
|
|
// wait for instance to be removed
|
|
err = suite.WaitInstanceToBeRemoved(instance.Name, 5*time.Minute)
|
|
suite.NoError(err, "error waiting for instance to be removed")
|
|
|
|
// wait for GARM to rebuild the pool running idle instances
|
|
err = suite.WaitPoolInstances(instance.PoolID, commonParams.InstanceRunning, params.RunnerIdle, 5*time.Minute)
|
|
suite.NoError(err, "error waiting for pool instances to be running idle")
|
|
}
|
|
|
|
func (suite *GarmSuite) waitLabelledJob(label string, timeout time.Duration) (*params.Job, error) {
|
|
t := suite.T()
|
|
var timeWaited time.Duration // default is 0
|
|
var jobs params.Jobs
|
|
var err error
|
|
|
|
t.Logf("Waiting for job with label %s", label)
|
|
for timeWaited < timeout {
|
|
jobs, err = listJobs(suite.cli, suite.authToken)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, job := range jobs {
|
|
for _, jobLabel := range job.Labels {
|
|
if jobLabel == label {
|
|
return &job, err
|
|
}
|
|
}
|
|
}
|
|
time.Sleep(5 * time.Second)
|
|
timeWaited += 5 * time.Second
|
|
}
|
|
|
|
if err := printJSONResponse(jobs); err != nil {
|
|
return nil, err
|
|
}
|
|
return nil, fmt.Errorf("failed to wait job with label %s", label)
|
|
}
|
|
|
|
func (suite *GarmSuite) waitJobStatus(id int64, status params.JobStatus, timeout time.Duration) (*params.Job, error) {
|
|
t := suite.T()
|
|
var timeWaited time.Duration // default is 0
|
|
var job *params.Job
|
|
|
|
t.Logf("Waiting for job %d to reach status %v", id, status)
|
|
for timeWaited < timeout {
|
|
jobs, err := listJobs(suite.cli, suite.authToken)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
job = nil
|
|
for k, v := range jobs {
|
|
if v.ID == id {
|
|
job = &jobs[k]
|
|
break
|
|
}
|
|
}
|
|
|
|
if job == nil {
|
|
if status == params.JobStatusCompleted {
|
|
// The job is not found in the list. We can safely assume
|
|
// that it is completed
|
|
return nil, nil
|
|
}
|
|
// if the job is not found, and expected status is not "completed",
|
|
// we need to error out.
|
|
return nil, fmt.Errorf("job %d not found, expected to be found in status %s", id, status)
|
|
} else if job.Status == string(status) {
|
|
return job, nil
|
|
}
|
|
time.Sleep(5 * time.Second)
|
|
timeWaited += 5 * time.Second
|
|
}
|
|
|
|
if err := printJSONResponse(*job); err != nil {
|
|
return nil, err
|
|
}
|
|
return nil, fmt.Errorf("timeout waiting for job %d to reach status %s", id, status)
|
|
}
|
|
|
|
func (suite *GarmSuite) waitInstanceStatus(name string, status commonParams.InstanceStatus, runnerStatus params.RunnerStatus, timeout time.Duration) (*params.Instance, error) {
|
|
t := suite.T()
|
|
var timeWaited time.Duration // default is 0
|
|
var instance *params.Instance
|
|
var err error
|
|
|
|
t.Logf("Waiting for instance %s to reach desired status %v and desired runner status %v", name, status, runnerStatus)
|
|
for timeWaited < timeout {
|
|
instance, err = getInstance(suite.cli, suite.authToken, name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
t.Logf("Instance %s has status %v and runner status %v", name, instance.Status, instance.RunnerStatus)
|
|
if instance.Status == status && instance.RunnerStatus == runnerStatus {
|
|
return instance, nil
|
|
}
|
|
time.Sleep(5 * time.Second)
|
|
timeWaited += 5 * time.Second
|
|
}
|
|
|
|
if err := printJSONResponse(*instance); err != nil {
|
|
return nil, err
|
|
}
|
|
return nil, fmt.Errorf("timeout waiting for instance %s status to reach status %s and runner status %s", name, status, runnerStatus)
|
|
}
|