Add functions to (un)install webhooks for orgs and repos
Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
This commit is contained in:
parent
f2796f1d5a
commit
7ce3f007b0
27 changed files with 1366 additions and 81 deletions
|
|
@ -48,7 +48,7 @@ func NewAPIController(r *runner.Runner, authenticator *auth.Authenticator, hub *
|
|||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 16384,
|
||||
},
|
||||
controllerID: controllerInfo.ShortControllerID(),
|
||||
controllerID: controllerInfo.ControllerID.String(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -479,3 +479,96 @@ func (a *APIController) UpdateOrgPoolHandler(w http.ResponseWriter, r *http.Requ
|
|||
log.Printf("failed to encode response: %q", err)
|
||||
}
|
||||
}
|
||||
|
||||
// swagger:route POST /organizations/{orgID}/webhook organizations hooks InstallOrgWebhook
|
||||
//
|
||||
// Install the GARM webhook for an organization. The secret configured on the organization will
|
||||
// be used to validate the requests.
|
||||
//
|
||||
// Parameters:
|
||||
// + name: orgID
|
||||
// description: Organization ID.
|
||||
// type: string
|
||||
// in: path
|
||||
// required: true
|
||||
//
|
||||
// + name: Body
|
||||
// description: Parameters used when creating the organization webhook.
|
||||
// type: InstallWebhookParams
|
||||
// in: body
|
||||
// required: true
|
||||
//
|
||||
// Responses:
|
||||
// 200: {}
|
||||
// default: APIErrorResponse
|
||||
func (a *APIController) InstallOrgWebhookHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
vars := mux.Vars(r)
|
||||
orgID, orgOk := vars["orgID"]
|
||||
if !orgOk {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
if err := json.NewEncoder(w).Encode(params.APIErrorResponse{
|
||||
Error: "Bad Request",
|
||||
Details: "No org ID specified",
|
||||
}); err != nil {
|
||||
log.Printf("failed to encode response: %q", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var hookParam runnerParams.InstallWebhookParams
|
||||
if err := json.NewDecoder(r.Body).Decode(&hookParam); err != nil {
|
||||
log.Printf("failed to decode: %s", err)
|
||||
handleError(w, gErrors.ErrBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if err := a.r.InstallOrgWebhook(ctx, orgID, hookParam); err != nil {
|
||||
log.Printf("installing webhook: %s", err)
|
||||
handleError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
// swagger:route DELETE /organizations/{orgID}/webhook organizations hooks UninstallOrgWebhook
|
||||
//
|
||||
// Uninstall organization webhook.
|
||||
//
|
||||
// Parameters:
|
||||
// + name: orgID
|
||||
// description: Organization ID.
|
||||
// type: string
|
||||
// in: path
|
||||
// required: true
|
||||
//
|
||||
// Responses:
|
||||
// default: APIErrorResponse
|
||||
func (a *APIController) UninstallOrgWebhookHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
vars := mux.Vars(r)
|
||||
orgID, orgOk := vars["orgID"]
|
||||
if !orgOk {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
if err := json.NewEncoder(w).Encode(params.APIErrorResponse{
|
||||
Error: "Bad Request",
|
||||
Details: "No org ID specified",
|
||||
}); err != nil {
|
||||
log.Printf("failed to encode response: %q", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err := a.r.UninstallOrgWebhook(ctx, orgID); err != nil {
|
||||
log.Printf("removing webhook: %s", err)
|
||||
handleError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -479,3 +479,96 @@ func (a *APIController) UpdateRepoPoolHandler(w http.ResponseWriter, r *http.Req
|
|||
log.Printf("failed to encode response: %q", err)
|
||||
}
|
||||
}
|
||||
|
||||
// swagger:route POST /repositories/{repoID}/webhook repositories hooks InstallRepoWebhook
|
||||
//
|
||||
// Install the GARM webhook for an organization. The secret configured on the organization will
|
||||
// be used to validate the requests.
|
||||
//
|
||||
// Parameters:
|
||||
// + name: repoID
|
||||
// description: Repository ID.
|
||||
// type: string
|
||||
// in: path
|
||||
// required: true
|
||||
//
|
||||
// + name: Body
|
||||
// description: Parameters used when creating the repository webhook.
|
||||
// type: InstallWebhookParams
|
||||
// in: body
|
||||
// required: true
|
||||
//
|
||||
// Responses:
|
||||
// 200: {}
|
||||
// default: APIErrorResponse
|
||||
func (a *APIController) InstallRepoWebhookHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
vars := mux.Vars(r)
|
||||
repoID, orgOk := vars["repoID"]
|
||||
if !orgOk {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
if err := json.NewEncoder(w).Encode(params.APIErrorResponse{
|
||||
Error: "Bad Request",
|
||||
Details: "No repository ID specified",
|
||||
}); err != nil {
|
||||
log.Printf("failed to encode response: %q", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var hookParam runnerParams.InstallWebhookParams
|
||||
if err := json.NewDecoder(r.Body).Decode(&hookParam); err != nil {
|
||||
log.Printf("failed to decode: %s", err)
|
||||
handleError(w, gErrors.ErrBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if err := a.r.InstallRepoWebhook(ctx, repoID, hookParam); err != nil {
|
||||
log.Printf("installing webhook: %s", err)
|
||||
handleError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
// swagger:route DELETE /repositories/{repoID}/webhook repositories hooks UninstallRepoWebhook
|
||||
//
|
||||
// Uninstall organization webhook.
|
||||
//
|
||||
// Parameters:
|
||||
// + name: repoID
|
||||
// description: Repository ID.
|
||||
// type: string
|
||||
// in: path
|
||||
// required: true
|
||||
//
|
||||
// Responses:
|
||||
// default: APIErrorResponse
|
||||
func (a *APIController) UninstallRepoWebhookHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
vars := mux.Vars(r)
|
||||
repoID, orgOk := vars["repoID"]
|
||||
if !orgOk {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
if err := json.NewEncoder(w).Encode(params.APIErrorResponse{
|
||||
Error: "Bad Request",
|
||||
Details: "No repository ID specified",
|
||||
}); err != nil {
|
||||
log.Printf("failed to encode response: %q", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err := a.r.UninstallRepoWebhook(ctx, repoID); err != nil {
|
||||
log.Printf("removing webhook: %s", err)
|
||||
handleError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -203,6 +203,13 @@ func NewAPIRouter(han *controllers.APIController, logWriter io.Writer, authMiddl
|
|||
apiRouter.Handle("/repositories/", http.HandlerFunc(han.CreateRepoHandler)).Methods("POST", "OPTIONS")
|
||||
apiRouter.Handle("/repositories", http.HandlerFunc(han.CreateRepoHandler)).Methods("POST", "OPTIONS")
|
||||
|
||||
// Install Webhook
|
||||
apiRouter.Handle("/repositories/{repoID}/webhook/", http.HandlerFunc(han.InstallRepoWebhookHandler)).Methods("POST", "OPTIONS")
|
||||
apiRouter.Handle("/repositories/{repoID}/webhook", http.HandlerFunc(han.InstallRepoWebhookHandler)).Methods("POST", "OPTIONS")
|
||||
// Uninstall Webhook
|
||||
apiRouter.Handle("/repositories/{repoID}/webhook/", http.HandlerFunc(han.UninstallRepoWebhookHandler)).Methods("DELETE", "OPTIONS")
|
||||
apiRouter.Handle("/repositories/{repoID}/webhook", http.HandlerFunc(han.UninstallRepoWebhookHandler)).Methods("DELETE", "OPTIONS")
|
||||
|
||||
/////////////////////////////
|
||||
// Organizations and pools //
|
||||
/////////////////////////////
|
||||
|
|
@ -242,6 +249,13 @@ func NewAPIRouter(han *controllers.APIController, logWriter io.Writer, authMiddl
|
|||
apiRouter.Handle("/organizations/", http.HandlerFunc(han.CreateOrgHandler)).Methods("POST", "OPTIONS")
|
||||
apiRouter.Handle("/organizations", http.HandlerFunc(han.CreateOrgHandler)).Methods("POST", "OPTIONS")
|
||||
|
||||
// Install Webhook
|
||||
apiRouter.Handle("/organizations/{orgID}/webhook/", http.HandlerFunc(han.InstallOrgWebhookHandler)).Methods("POST", "OPTIONS")
|
||||
apiRouter.Handle("/organizations/{orgID}/webhook", http.HandlerFunc(han.InstallOrgWebhookHandler)).Methods("POST", "OPTIONS")
|
||||
// Uninstall Webhook
|
||||
apiRouter.Handle("/organizations/{orgID}/webhook/", http.HandlerFunc(han.UninstallOrgWebhookHandler)).Methods("DELETE", "OPTIONS")
|
||||
apiRouter.Handle("/organizations/{orgID}/webhook", http.HandlerFunc(han.UninstallOrgWebhookHandler)).Methods("DELETE", "OPTIONS")
|
||||
|
||||
/////////////////////////////
|
||||
// Enterprises and pools //
|
||||
/////////////////////////////
|
||||
|
|
|
|||
|
|
@ -15,6 +15,13 @@ definitions:
|
|||
import:
|
||||
package: github.com/cloudbase/garm/params
|
||||
alias: garm_params
|
||||
InstallWebhookParams:
|
||||
type: object
|
||||
x-go-type:
|
||||
type: InstallWebhookParams
|
||||
import:
|
||||
package: github.com/cloudbase/garm/params
|
||||
alias: garm_params
|
||||
NewUserParams:
|
||||
type: object
|
||||
x-go-type:
|
||||
|
|
|
|||
|
|
@ -76,6 +76,13 @@ definitions:
|
|||
alias: garm_params
|
||||
package: github.com/cloudbase/garm/params
|
||||
type: GithubCredentials
|
||||
InstallWebhookParams:
|
||||
type: object
|
||||
x-go-type:
|
||||
import:
|
||||
alias: garm_params
|
||||
package: github.com/cloudbase/garm/params
|
||||
type: InstallWebhookParams
|
||||
Instance:
|
||||
type: object
|
||||
x-go-type:
|
||||
|
|
@ -900,6 +907,53 @@ paths:
|
|||
tags:
|
||||
- organizations
|
||||
- pools
|
||||
/organizations/{orgID}/webhook:
|
||||
delete:
|
||||
operationId: UninstallOrgWebhook
|
||||
parameters:
|
||||
- description: Organization ID.
|
||||
in: path
|
||||
name: orgID
|
||||
required: true
|
||||
type: string
|
||||
responses:
|
||||
default:
|
||||
description: APIErrorResponse
|
||||
schema:
|
||||
$ref: '#/definitions/APIErrorResponse'
|
||||
summary: Uninstall organization webhook.
|
||||
tags:
|
||||
- organizations
|
||||
- hooks
|
||||
post:
|
||||
description: |-
|
||||
Install the GARM webhook for an organization. The secret configured on the organization will
|
||||
be used to validate the requests.
|
||||
operationId: InstallOrgWebhook
|
||||
parameters:
|
||||
- description: Organization ID.
|
||||
in: path
|
||||
name: orgID
|
||||
required: true
|
||||
type: string
|
||||
- description: Parameters used when creating the organization webhook.
|
||||
in: body
|
||||
name: Body
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/InstallWebhookParams'
|
||||
description: Parameters used when creating the organization webhook.
|
||||
type: object
|
||||
responses:
|
||||
"200":
|
||||
$ref: '#/responses/%7B%7D'
|
||||
default:
|
||||
description: APIErrorResponse
|
||||
schema:
|
||||
$ref: '#/definitions/APIErrorResponse'
|
||||
tags:
|
||||
- organizations
|
||||
- hooks
|
||||
/pools:
|
||||
get:
|
||||
operationId: ListPools
|
||||
|
|
@ -1275,6 +1329,53 @@ paths:
|
|||
tags:
|
||||
- repositories
|
||||
- pools
|
||||
/repositories/{repoID}/webhook:
|
||||
delete:
|
||||
operationId: UninstallRepoWebhook
|
||||
parameters:
|
||||
- description: Repository ID.
|
||||
in: path
|
||||
name: repoID
|
||||
required: true
|
||||
type: string
|
||||
responses:
|
||||
default:
|
||||
description: APIErrorResponse
|
||||
schema:
|
||||
$ref: '#/definitions/APIErrorResponse'
|
||||
summary: Uninstall organization webhook.
|
||||
tags:
|
||||
- repositories
|
||||
- hooks
|
||||
post:
|
||||
description: |-
|
||||
Install the GARM webhook for an organization. The secret configured on the organization will
|
||||
be used to validate the requests.
|
||||
operationId: InstallRepoWebhook
|
||||
parameters:
|
||||
- description: Repository ID.
|
||||
in: path
|
||||
name: repoID
|
||||
required: true
|
||||
type: string
|
||||
- description: Parameters used when creating the repository webhook.
|
||||
in: body
|
||||
name: Body
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/InstallWebhookParams'
|
||||
description: Parameters used when creating the repository webhook.
|
||||
type: object
|
||||
responses:
|
||||
"200":
|
||||
$ref: '#/responses/%7B%7D'
|
||||
default:
|
||||
description: APIErrorResponse
|
||||
schema:
|
||||
$ref: '#/definitions/APIErrorResponse'
|
||||
tags:
|
||||
- repositories
|
||||
- hooks
|
||||
produces:
|
||||
- application/json
|
||||
security:
|
||||
|
|
|
|||
|
|
@ -46,22 +46,32 @@ var infoShowCmd = &cobra.Command{
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
formatInfo(response.Payload)
|
||||
return nil
|
||||
return formatInfo(response.Payload)
|
||||
},
|
||||
}
|
||||
|
||||
func formatInfo(info params.ControllerInfo) {
|
||||
func formatInfo(info params.ControllerInfo) error {
|
||||
t := table.NewWriter()
|
||||
|
||||
header := table.Row{"Field", "Value"}
|
||||
|
||||
if info.WebhookURL == "" {
|
||||
info.WebhookURL = "N/A"
|
||||
}
|
||||
|
||||
if info.ControllerWebhookURL == "" {
|
||||
info.ControllerWebhookURL = "N/A"
|
||||
}
|
||||
|
||||
t.AppendHeader(header)
|
||||
t.AppendRow(table.Row{"Controller ID", info.ControllerID})
|
||||
t.AppendRow(table.Row{"Hostname", info.Hostname})
|
||||
t.AppendRow(table.Row{"Metadata URL", info.MetadataURL})
|
||||
t.AppendRow(table.Row{"Callback URL", info.CallbackURL})
|
||||
t.AppendRow(table.Row{"Webhook URL Base", info.WebhookURL})
|
||||
t.AppendRow(table.Row{"Controller Webhook URL", info.ControllerWebhookURL})
|
||||
fmt.Println(t.Render())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ package cmd
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cloudbase/garm-provider-common/util"
|
||||
apiClientEnterprises "github.com/cloudbase/garm/client/enterprises"
|
||||
"github.com/cloudbase/garm/params"
|
||||
|
||||
|
|
@ -25,9 +26,10 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
enterpriseName string
|
||||
enterpriseWebhookSecret string
|
||||
enterpriseCreds string
|
||||
enterpriseName string
|
||||
enterpriseWebhookSecret string
|
||||
enterpriseCreds string
|
||||
enterpriseRandomWebhookSecret bool
|
||||
)
|
||||
|
||||
// enterpriseCmd represents the enterprise command
|
||||
|
|
@ -55,6 +57,14 @@ var enterpriseAddCmd = &cobra.Command{
|
|||
return errNeedsInitError
|
||||
}
|
||||
|
||||
if enterpriseRandomWebhookSecret {
|
||||
secret, err := util.GetRandomString(32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
enterpriseWebhookSecret = secret
|
||||
}
|
||||
|
||||
newEnterpriseReq := apiClientEnterprises.NewCreateEnterpriseParams()
|
||||
newEnterpriseReq.Body = params.CreateEnterpriseParams{
|
||||
Name: enterpriseName,
|
||||
|
|
@ -179,6 +189,9 @@ func init() {
|
|||
enterpriseAddCmd.Flags().StringVar(&enterpriseName, "name", "", "The name of the enterprise")
|
||||
enterpriseAddCmd.Flags().StringVar(&enterpriseWebhookSecret, "webhook-secret", "", "The webhook secret for this enterprise")
|
||||
enterpriseAddCmd.Flags().StringVar(&enterpriseCreds, "credentials", "", "Credentials name. See credentials list.")
|
||||
enterpriseAddCmd.Flags().BoolVar(&enterpriseRandomWebhookSecret, "random-webhook-secret", false, "Generate a random webhook secret for this organization.")
|
||||
enterpriseAddCmd.MarkFlagsMutuallyExclusive("webhook-secret", "random-webhook-secret")
|
||||
|
||||
enterpriseAddCmd.MarkFlagRequired("credentials") //nolint
|
||||
enterpriseAddCmd.MarkFlagRequired("name") //nolint
|
||||
enterpriseUpdateCmd.Flags().StringVar(&enterpriseWebhookSecret, "webhook-secret", "", "The webhook secret for this enterprise")
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ package cmd
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cloudbase/garm-provider-common/util"
|
||||
apiClientOrgs "github.com/cloudbase/garm/client/organizations"
|
||||
"github.com/cloudbase/garm/params"
|
||||
|
||||
|
|
@ -25,9 +26,10 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
orgName string
|
||||
orgWebhookSecret string
|
||||
orgCreds string
|
||||
orgName string
|
||||
orgWebhookSecret string
|
||||
orgCreds string
|
||||
orgRandomWebhookSecret bool
|
||||
)
|
||||
|
||||
// organizationCmd represents the organization command
|
||||
|
|
@ -55,6 +57,14 @@ var orgAddCmd = &cobra.Command{
|
|||
return errNeedsInitError
|
||||
}
|
||||
|
||||
if orgRandomWebhookSecret {
|
||||
secret, err := util.GetRandomString(32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
orgWebhookSecret = secret
|
||||
}
|
||||
|
||||
newOrgReq := apiClientOrgs.NewCreateOrgParams()
|
||||
newOrgReq.Body = params.CreateOrgParams{
|
||||
Name: orgName,
|
||||
|
|
@ -179,6 +189,9 @@ func init() {
|
|||
orgAddCmd.Flags().StringVar(&orgName, "name", "", "The name of the organization")
|
||||
orgAddCmd.Flags().StringVar(&orgWebhookSecret, "webhook-secret", "", "The webhook secret for this organization")
|
||||
orgAddCmd.Flags().StringVar(&orgCreds, "credentials", "", "Credentials name. See credentials list.")
|
||||
orgAddCmd.Flags().BoolVar(&orgRandomWebhookSecret, "random-webhook-secret", false, "Generate a random webhook secret for this organization.")
|
||||
orgAddCmd.MarkFlagsMutuallyExclusive("webhook-secret", "random-webhook-secret")
|
||||
|
||||
orgAddCmd.MarkFlagRequired("credentials") //nolint
|
||||
orgAddCmd.MarkFlagRequired("name") //nolint
|
||||
orgUpdateCmd.Flags().StringVar(&orgWebhookSecret, "webhook-secret", "", "The webhook secret for this organization")
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ package cmd
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cloudbase/garm-provider-common/util"
|
||||
apiClientRepos "github.com/cloudbase/garm/client/repositories"
|
||||
"github.com/cloudbase/garm/params"
|
||||
|
||||
|
|
@ -25,10 +26,11 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
repoOwner string
|
||||
repoName string
|
||||
repoWebhookSecret string
|
||||
repoCreds string
|
||||
repoOwner string
|
||||
repoName string
|
||||
repoWebhookSecret string
|
||||
repoCreds string
|
||||
randomWebhookSecret bool
|
||||
)
|
||||
|
||||
// repositoryCmd represents the repository command
|
||||
|
|
@ -56,6 +58,14 @@ var repoAddCmd = &cobra.Command{
|
|||
return errNeedsInitError
|
||||
}
|
||||
|
||||
if randomWebhookSecret {
|
||||
secret, err := util.GetRandomString(32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
repoWebhookSecret = secret
|
||||
}
|
||||
|
||||
newRepoReq := apiClientRepos.NewCreateRepoParams()
|
||||
newRepoReq.Body = params.CreateRepoParams{
|
||||
Owner: repoOwner,
|
||||
|
|
@ -183,10 +193,13 @@ func init() {
|
|||
repoAddCmd.Flags().StringVar(&repoName, "name", "", "The name of the repository")
|
||||
repoAddCmd.Flags().StringVar(&repoWebhookSecret, "webhook-secret", "", "The webhook secret for this repository")
|
||||
repoAddCmd.Flags().StringVar(&repoCreds, "credentials", "", "Credentials name. See credentials list.")
|
||||
repoAddCmd.Flags().BoolVar(&randomWebhookSecret, "random-webhook-secret", false, "Generate a random webhook secret for this repository.")
|
||||
repoAddCmd.MarkFlagsMutuallyExclusive("webhook-secret", "random-webhook-secret")
|
||||
|
||||
repoAddCmd.MarkFlagRequired("credentials") //nolint
|
||||
repoAddCmd.MarkFlagRequired("owner") //nolint
|
||||
repoAddCmd.MarkFlagRequired("name") //nolint
|
||||
repoUpdateCmd.Flags().StringVar(&repoWebhookSecret, "webhook-secret", "", "The webhook secret for this repository")
|
||||
repoUpdateCmd.Flags().StringVar(&repoWebhookSecret, "webhook-secret", "", "The webhook secret for this repository. If you update this secret, you will have to manually update the secret in GitHub as well.")
|
||||
repoUpdateCmd.Flags().StringVar(&repoCreds, "credentials", "", "Credentials name. See credentials list.")
|
||||
|
||||
repositoryCmd.AddCommand(
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ package params
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
commonParams "github.com/cloudbase/garm-provider-common/params"
|
||||
|
|
@ -28,12 +27,13 @@ import (
|
|||
)
|
||||
|
||||
type (
|
||||
PoolType string
|
||||
EventType string
|
||||
EventLevel string
|
||||
ProviderType string
|
||||
JobStatus string
|
||||
RunnerStatus string
|
||||
PoolType string
|
||||
EventType string
|
||||
EventLevel string
|
||||
ProviderType string
|
||||
JobStatus string
|
||||
RunnerStatus string
|
||||
WebhookEndpointType string
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -43,6 +43,16 @@ const (
|
|||
ExternalProvider ProviderType = "external"
|
||||
)
|
||||
|
||||
const (
|
||||
// WebhookEndpointDirect instructs garm that it should attempt to create a webhook
|
||||
// in the target entity, using the callback URL defined in the config as a target.
|
||||
WebhookEndpointDirect WebhookEndpointType = "direct"
|
||||
// WebhookEndpointTunnel instructs garm that it should attempt to create a webhook
|
||||
// in the target entity, using the tunnel URL as a base for the webhook URL.
|
||||
// This is defined for future use.
|
||||
WebhookEndpointTunnel WebhookEndpointType = "tunnel"
|
||||
)
|
||||
|
||||
const (
|
||||
JobStatusQueued JobStatus = "queued"
|
||||
JobStatusInProgress JobStatus = "in_progress"
|
||||
|
|
@ -286,11 +296,14 @@ func (p *Pool) PoolType() PoolType {
|
|||
type Pools []Pool
|
||||
|
||||
type Internal struct {
|
||||
OAuth2Token string `json:"oauth2"`
|
||||
ControllerID string `json:"controller_id"`
|
||||
InstanceCallbackURL string `json:"instance_callback_url"`
|
||||
InstanceMetadataURL string `json:"instance_metadata_url"`
|
||||
JWTSecret string `json:"jwt_secret"`
|
||||
OAuth2Token string `json:"oauth2"`
|
||||
ControllerID string `json:"controller_id"`
|
||||
InstanceCallbackURL string `json:"instance_callback_url"`
|
||||
InstanceMetadataURL string `json:"instance_metadata_url"`
|
||||
BaseWebhookURL string `json:"base_webhook_url"`
|
||||
ControllerWebhookURL string `json:"controller_webhook_url"`
|
||||
|
||||
JWTSecret string `json:"jwt_secret"`
|
||||
// GithubCredentialsDetails contains all info about the credentials, except the
|
||||
// token, which is added above.
|
||||
GithubCredentialsDetails GithubCredentials `json:"gh_creds_details"`
|
||||
|
|
@ -380,20 +393,12 @@ type JWTResponse struct {
|
|||
}
|
||||
|
||||
type ControllerInfo struct {
|
||||
ControllerID uuid.UUID `json:"controller_id"`
|
||||
Hostname string `json:"hostname"`
|
||||
MetadataURL string `json:"metadata_url"`
|
||||
CallbackURL string `json:"callback_url"`
|
||||
}
|
||||
|
||||
func (c ControllerInfo) ShortControllerID() string {
|
||||
var i big.Int
|
||||
i.SetBytes(c.ControllerID[:])
|
||||
id := i.Text(62)
|
||||
if id == "0" {
|
||||
return ""
|
||||
}
|
||||
return id
|
||||
ControllerID uuid.UUID `json:"controller_id"`
|
||||
Hostname string `json:"hostname"`
|
||||
MetadataURL string `json:"metadata_url"`
|
||||
CallbackURL string `json:"callback_url"`
|
||||
WebhookURL string `json:"webhook_url"`
|
||||
ControllerWebhookURL string `json:"controller_webhook_url"`
|
||||
}
|
||||
|
||||
type GithubCredentials struct {
|
||||
|
|
@ -495,3 +500,8 @@ type Job struct {
|
|||
|
||||
// used by swagger client generated code
|
||||
type Jobs []Job
|
||||
|
||||
type InstallWebhookParams struct {
|
||||
WebhookEndpointType WebhookEndpointType `json:"webhook_endpoint_type"`
|
||||
InsecureSSL bool `json:"insecure_ssl"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,41 @@ type GithubClient struct {
|
|||
mock.Mock
|
||||
}
|
||||
|
||||
// CreateOrgHook provides a mock function with given fields: ctx, org, hook
|
||||
func (_m *GithubClient) CreateOrgHook(ctx context.Context, org string, hook *github.Hook) (*github.Hook, *github.Response, error) {
|
||||
ret := _m.Called(ctx, org, hook)
|
||||
|
||||
var r0 *github.Hook
|
||||
var r1 *github.Response
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, *github.Hook) (*github.Hook, *github.Response, error)); ok {
|
||||
return rf(ctx, org, hook)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, *github.Hook) *github.Hook); ok {
|
||||
r0 = rf(ctx, org, hook)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.Hook)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, *github.Hook) *github.Response); ok {
|
||||
r1 = rf(ctx, org, hook)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context, string, *github.Hook) error); ok {
|
||||
r2 = rf(ctx, org, hook)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// CreateOrganizationRegistrationToken provides a mock function with given fields: ctx, owner
|
||||
func (_m *GithubClient) CreateOrganizationRegistrationToken(ctx context.Context, owner string) (*github.RegistrationToken, *github.Response, error) {
|
||||
ret := _m.Called(ctx, owner)
|
||||
|
|
@ -84,6 +119,163 @@ func (_m *GithubClient) CreateRegistrationToken(ctx context.Context, owner strin
|
|||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// CreateRepoHook provides a mock function with given fields: ctx, owner, repo, hook
|
||||
func (_m *GithubClient) CreateRepoHook(ctx context.Context, owner string, repo string, hook *github.Hook) (*github.Hook, *github.Response, error) {
|
||||
ret := _m.Called(ctx, owner, repo, hook)
|
||||
|
||||
var r0 *github.Hook
|
||||
var r1 *github.Response
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, *github.Hook) (*github.Hook, *github.Response, error)); ok {
|
||||
return rf(ctx, owner, repo, hook)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, *github.Hook) *github.Hook); ok {
|
||||
r0 = rf(ctx, owner, repo, hook)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.Hook)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, string, *github.Hook) *github.Response); ok {
|
||||
r1 = rf(ctx, owner, repo, hook)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context, string, string, *github.Hook) error); ok {
|
||||
r2 = rf(ctx, owner, repo, hook)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// DeleteOrgHook provides a mock function with given fields: ctx, org, id
|
||||
func (_m *GithubClient) DeleteOrgHook(ctx context.Context, org string, id int64) (*github.Response, error) {
|
||||
ret := _m.Called(ctx, org, id)
|
||||
|
||||
var r0 *github.Response
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, int64) (*github.Response, error)); ok {
|
||||
return rf(ctx, org, id)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, int64) *github.Response); ok {
|
||||
r0 = rf(ctx, org, id)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, int64) error); ok {
|
||||
r1 = rf(ctx, org, id)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// DeleteRepoHook provides a mock function with given fields: ctx, owner, repo, id
|
||||
func (_m *GithubClient) DeleteRepoHook(ctx context.Context, owner string, repo string, id int64) (*github.Response, error) {
|
||||
ret := _m.Called(ctx, owner, repo, id)
|
||||
|
||||
var r0 *github.Response
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, int64) (*github.Response, error)); ok {
|
||||
return rf(ctx, owner, repo, id)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, int64) *github.Response); ok {
|
||||
r0 = rf(ctx, owner, repo, id)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, string, int64) error); ok {
|
||||
r1 = rf(ctx, owner, repo, id)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetOrgHook provides a mock function with given fields: ctx, org, id
|
||||
func (_m *GithubClient) GetOrgHook(ctx context.Context, org string, id int64) (*github.Hook, *github.Response, error) {
|
||||
ret := _m.Called(ctx, org, id)
|
||||
|
||||
var r0 *github.Hook
|
||||
var r1 *github.Response
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, int64) (*github.Hook, *github.Response, error)); ok {
|
||||
return rf(ctx, org, id)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, int64) *github.Hook); ok {
|
||||
r0 = rf(ctx, org, id)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.Hook)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, int64) *github.Response); ok {
|
||||
r1 = rf(ctx, org, id)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context, string, int64) error); ok {
|
||||
r2 = rf(ctx, org, id)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// GetRepoHook provides a mock function with given fields: ctx, owner, repo, id
|
||||
func (_m *GithubClient) GetRepoHook(ctx context.Context, owner string, repo string, id int64) (*github.Hook, *github.Response, error) {
|
||||
ret := _m.Called(ctx, owner, repo, id)
|
||||
|
||||
var r0 *github.Hook
|
||||
var r1 *github.Response
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, int64) (*github.Hook, *github.Response, error)); ok {
|
||||
return rf(ctx, owner, repo, id)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, int64) *github.Hook); ok {
|
||||
r0 = rf(ctx, owner, repo, id)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.Hook)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, string, int64) *github.Response); ok {
|
||||
r1 = rf(ctx, owner, repo, id)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context, string, string, int64) error); ok {
|
||||
r2 = rf(ctx, owner, repo, id)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// GetWorkflowJobByID provides a mock function with given fields: ctx, owner, repo, jobID
|
||||
func (_m *GithubClient) GetWorkflowJobByID(ctx context.Context, owner string, repo string, jobID int64) (*github.WorkflowJob, *github.Response, error) {
|
||||
ret := _m.Called(ctx, owner, repo, jobID)
|
||||
|
|
@ -119,6 +311,41 @@ func (_m *GithubClient) GetWorkflowJobByID(ctx context.Context, owner string, re
|
|||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// ListOrgHooks provides a mock function with given fields: ctx, org, opts
|
||||
func (_m *GithubClient) ListOrgHooks(ctx context.Context, org string, opts *github.ListOptions) ([]*github.Hook, *github.Response, error) {
|
||||
ret := _m.Called(ctx, org, opts)
|
||||
|
||||
var r0 []*github.Hook
|
||||
var r1 *github.Response
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, *github.ListOptions) ([]*github.Hook, *github.Response, error)); ok {
|
||||
return rf(ctx, org, opts)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, *github.ListOptions) []*github.Hook); ok {
|
||||
r0 = rf(ctx, org, opts)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*github.Hook)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, *github.ListOptions) *github.Response); ok {
|
||||
r1 = rf(ctx, org, opts)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context, string, *github.ListOptions) error); ok {
|
||||
r2 = rf(ctx, org, opts)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// ListOrganizationRunnerApplicationDownloads provides a mock function with given fields: ctx, owner
|
||||
func (_m *GithubClient) ListOrganizationRunnerApplicationDownloads(ctx context.Context, owner string) ([]*github.RunnerApplicationDownload, *github.Response, error) {
|
||||
ret := _m.Called(ctx, owner)
|
||||
|
|
@ -189,6 +416,41 @@ func (_m *GithubClient) ListOrganizationRunners(ctx context.Context, owner strin
|
|||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// ListRepoHooks provides a mock function with given fields: ctx, owner, repo, opts
|
||||
func (_m *GithubClient) ListRepoHooks(ctx context.Context, owner string, repo string, opts *github.ListOptions) ([]*github.Hook, *github.Response, error) {
|
||||
ret := _m.Called(ctx, owner, repo, opts)
|
||||
|
||||
var r0 []*github.Hook
|
||||
var r1 *github.Response
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, *github.ListOptions) ([]*github.Hook, *github.Response, error)); ok {
|
||||
return rf(ctx, owner, repo, opts)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, *github.ListOptions) []*github.Hook); ok {
|
||||
r0 = rf(ctx, owner, repo, opts)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*github.Hook)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, string, *github.ListOptions) *github.Response); ok {
|
||||
r1 = rf(ctx, owner, repo, opts)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context, string, string, *github.ListOptions) error); ok {
|
||||
r2 = rf(ctx, owner, repo, opts)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// ListRunnerApplicationDownloads provides a mock function with given fields: ctx, owner, repo
|
||||
func (_m *GithubClient) ListRunnerApplicationDownloads(ctx context.Context, owner string, repo string) ([]*github.RunnerApplicationDownload, *github.Response, error) {
|
||||
ret := _m.Called(ctx, owner, repo)
|
||||
|
|
|
|||
160
runner/common/mocks/OrganizationHooks.go
Normal file
160
runner/common/mocks/OrganizationHooks.go
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
// Code generated by mockery v0.0.0-dev. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
github "github.com/google/go-github/v53/github"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// OrganizationHooks is an autogenerated mock type for the OrganizationHooks type
|
||||
type OrganizationHooks struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// CreateOrgHook provides a mock function with given fields: ctx, org, hook
|
||||
func (_m *OrganizationHooks) CreateOrgHook(ctx context.Context, org string, hook *github.Hook) (*github.Hook, *github.Response, error) {
|
||||
ret := _m.Called(ctx, org, hook)
|
||||
|
||||
var r0 *github.Hook
|
||||
var r1 *github.Response
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, *github.Hook) (*github.Hook, *github.Response, error)); ok {
|
||||
return rf(ctx, org, hook)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, *github.Hook) *github.Hook); ok {
|
||||
r0 = rf(ctx, org, hook)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.Hook)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, *github.Hook) *github.Response); ok {
|
||||
r1 = rf(ctx, org, hook)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context, string, *github.Hook) error); ok {
|
||||
r2 = rf(ctx, org, hook)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// DeleteOrgHook provides a mock function with given fields: ctx, org, id
|
||||
func (_m *OrganizationHooks) DeleteOrgHook(ctx context.Context, org string, id int64) (*github.Response, error) {
|
||||
ret := _m.Called(ctx, org, id)
|
||||
|
||||
var r0 *github.Response
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, int64) (*github.Response, error)); ok {
|
||||
return rf(ctx, org, id)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, int64) *github.Response); ok {
|
||||
r0 = rf(ctx, org, id)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, int64) error); ok {
|
||||
r1 = rf(ctx, org, id)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetOrgHook provides a mock function with given fields: ctx, org, id
|
||||
func (_m *OrganizationHooks) GetOrgHook(ctx context.Context, org string, id int64) (*github.Hook, *github.Response, error) {
|
||||
ret := _m.Called(ctx, org, id)
|
||||
|
||||
var r0 *github.Hook
|
||||
var r1 *github.Response
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, int64) (*github.Hook, *github.Response, error)); ok {
|
||||
return rf(ctx, org, id)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, int64) *github.Hook); ok {
|
||||
r0 = rf(ctx, org, id)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.Hook)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, int64) *github.Response); ok {
|
||||
r1 = rf(ctx, org, id)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context, string, int64) error); ok {
|
||||
r2 = rf(ctx, org, id)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// ListOrgHooks provides a mock function with given fields: ctx, org, opts
|
||||
func (_m *OrganizationHooks) ListOrgHooks(ctx context.Context, org string, opts *github.ListOptions) ([]*github.Hook, *github.Response, error) {
|
||||
ret := _m.Called(ctx, org, opts)
|
||||
|
||||
var r0 []*github.Hook
|
||||
var r1 *github.Response
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, *github.ListOptions) ([]*github.Hook, *github.Response, error)); ok {
|
||||
return rf(ctx, org, opts)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, *github.ListOptions) []*github.Hook); ok {
|
||||
r0 = rf(ctx, org, opts)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*github.Hook)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, *github.ListOptions) *github.Response); ok {
|
||||
r1 = rf(ctx, org, opts)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context, string, *github.ListOptions) error); ok {
|
||||
r2 = rf(ctx, org, opts)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// NewOrganizationHooks creates a new instance of OrganizationHooks. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewOrganizationHooks(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *OrganizationHooks {
|
||||
mock := &OrganizationHooks{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
|
|
@ -3,6 +3,8 @@
|
|||
package mocks
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
params "github.com/cloudbase/garm/params"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
|
@ -78,6 +80,20 @@ func (_m *PoolManager) ID() string {
|
|||
return r0
|
||||
}
|
||||
|
||||
// InstallWebhook provides a mock function with given fields: ctx, param
|
||||
func (_m *PoolManager) InstallWebhook(ctx context.Context, param params.InstallWebhookParams) error {
|
||||
ret := _m.Called(ctx, param)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, params.InstallWebhookParams) error); ok {
|
||||
r0 = rf(ctx, param)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// RefreshState provides a mock function with given fields: param
|
||||
func (_m *PoolManager) RefreshState(param params.UpdatePoolStateParams) error {
|
||||
ret := _m.Called(param)
|
||||
|
|
@ -134,6 +150,20 @@ func (_m *PoolManager) Stop() error {
|
|||
return r0
|
||||
}
|
||||
|
||||
// UninstallWebhook provides a mock function with given fields: ctx
|
||||
func (_m *PoolManager) UninstallWebhook(ctx context.Context) error {
|
||||
ret := _m.Called(ctx)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context) error); ok {
|
||||
r0 = rf(ctx)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Wait provides a mock function with given fields:
|
||||
func (_m *PoolManager) Wait() error {
|
||||
ret := _m.Called()
|
||||
|
|
|
|||
160
runner/common/mocks/RepositoryHooks.go
Normal file
160
runner/common/mocks/RepositoryHooks.go
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
// Code generated by mockery v0.0.0-dev. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
github "github.com/google/go-github/v53/github"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// RepositoryHooks is an autogenerated mock type for the RepositoryHooks type
|
||||
type RepositoryHooks struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// CreateRepoHook provides a mock function with given fields: ctx, owner, repo, hook
|
||||
func (_m *RepositoryHooks) CreateRepoHook(ctx context.Context, owner string, repo string, hook *github.Hook) (*github.Hook, *github.Response, error) {
|
||||
ret := _m.Called(ctx, owner, repo, hook)
|
||||
|
||||
var r0 *github.Hook
|
||||
var r1 *github.Response
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, *github.Hook) (*github.Hook, *github.Response, error)); ok {
|
||||
return rf(ctx, owner, repo, hook)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, *github.Hook) *github.Hook); ok {
|
||||
r0 = rf(ctx, owner, repo, hook)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.Hook)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, string, *github.Hook) *github.Response); ok {
|
||||
r1 = rf(ctx, owner, repo, hook)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context, string, string, *github.Hook) error); ok {
|
||||
r2 = rf(ctx, owner, repo, hook)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// DeleteRepoHook provides a mock function with given fields: ctx, owner, repo, id
|
||||
func (_m *RepositoryHooks) DeleteRepoHook(ctx context.Context, owner string, repo string, id int64) (*github.Response, error) {
|
||||
ret := _m.Called(ctx, owner, repo, id)
|
||||
|
||||
var r0 *github.Response
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, int64) (*github.Response, error)); ok {
|
||||
return rf(ctx, owner, repo, id)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, int64) *github.Response); ok {
|
||||
r0 = rf(ctx, owner, repo, id)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, string, int64) error); ok {
|
||||
r1 = rf(ctx, owner, repo, id)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetRepoHook provides a mock function with given fields: ctx, owner, repo, id
|
||||
func (_m *RepositoryHooks) GetRepoHook(ctx context.Context, owner string, repo string, id int64) (*github.Hook, *github.Response, error) {
|
||||
ret := _m.Called(ctx, owner, repo, id)
|
||||
|
||||
var r0 *github.Hook
|
||||
var r1 *github.Response
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, int64) (*github.Hook, *github.Response, error)); ok {
|
||||
return rf(ctx, owner, repo, id)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, int64) *github.Hook); ok {
|
||||
r0 = rf(ctx, owner, repo, id)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.Hook)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, string, int64) *github.Response); ok {
|
||||
r1 = rf(ctx, owner, repo, id)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context, string, string, int64) error); ok {
|
||||
r2 = rf(ctx, owner, repo, id)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// ListRepoHooks provides a mock function with given fields: ctx, owner, repo, opts
|
||||
func (_m *RepositoryHooks) ListRepoHooks(ctx context.Context, owner string, repo string, opts *github.ListOptions) ([]*github.Hook, *github.Response, error) {
|
||||
ret := _m.Called(ctx, owner, repo, opts)
|
||||
|
||||
var r0 []*github.Hook
|
||||
var r1 *github.Response
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, *github.ListOptions) ([]*github.Hook, *github.Response, error)); ok {
|
||||
return rf(ctx, owner, repo, opts)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, *github.ListOptions) []*github.Hook); ok {
|
||||
r0 = rf(ctx, owner, repo, opts)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*github.Hook)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, string, *github.ListOptions) *github.Response); ok {
|
||||
r1 = rf(ctx, owner, repo, opts)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context, string, string, *github.ListOptions) error); ok {
|
||||
r2 = rf(ctx, owner, repo, opts)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// NewRepositoryHooks creates a new instance of RepositoryHooks. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewRepositoryHooks(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *RepositoryHooks {
|
||||
mock := &RepositoryHooks{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/cloudbase/garm/params"
|
||||
|
|
@ -43,7 +44,8 @@ type PoolManager interface {
|
|||
HandleWorkflowJob(job params.WorkflowJob) error
|
||||
RefreshState(param params.UpdatePoolStateParams) error
|
||||
ForceDeleteRunner(runner params.Instance) error
|
||||
// AddPool(ctx context.Context, pool params.Pool) error
|
||||
InstallWebhook(ctx context.Context, param params.InstallWebhookParams) error
|
||||
UninstallWebhook(ctx context.Context) error
|
||||
|
||||
// PoolManager lifecycle functions. Start/stop pool.
|
||||
Start() error
|
||||
|
|
|
|||
|
|
@ -6,11 +6,28 @@ import (
|
|||
"github.com/google/go-github/v53/github"
|
||||
)
|
||||
|
||||
type OrganizationHooks interface {
|
||||
ListOrgHooks(ctx context.Context, org string, opts *github.ListOptions) ([]*github.Hook, *github.Response, error)
|
||||
GetOrgHook(ctx context.Context, org string, id int64) (*github.Hook, *github.Response, error)
|
||||
CreateOrgHook(ctx context.Context, org string, hook *github.Hook) (*github.Hook, *github.Response, error)
|
||||
DeleteOrgHook(ctx context.Context, org string, id int64) (*github.Response, error)
|
||||
}
|
||||
|
||||
type RepositoryHooks interface {
|
||||
ListRepoHooks(ctx context.Context, owner, repo string, opts *github.ListOptions) ([]*github.Hook, *github.Response, error)
|
||||
GetRepoHook(ctx context.Context, owner, repo string, id int64) (*github.Hook, *github.Response, error)
|
||||
CreateRepoHook(ctx context.Context, owner, repo string, hook *github.Hook) (*github.Hook, *github.Response, error)
|
||||
DeleteRepoHook(ctx context.Context, owner, repo string, id int64) (*github.Response, error)
|
||||
}
|
||||
|
||||
// GithubClient that describes the minimum list of functions we need to interact with github.
|
||||
// Allows for easier testing.
|
||||
//
|
||||
//go:generate mockery --all
|
||||
type GithubClient interface {
|
||||
OrganizationHooks
|
||||
RepositoryHooks
|
||||
|
||||
// GetWorkflowJobByID gets details about a single workflow job.
|
||||
GetWorkflowJobByID(ctx context.Context, owner, repo string, jobID int64) (*github.WorkflowJob, *github.Response, error)
|
||||
// ListRunners lists all runners within a repository.
|
||||
|
|
|
|||
|
|
@ -339,3 +339,45 @@ func (r *Runner) findOrgPoolManager(name string) (common.PoolManager, error) {
|
|||
}
|
||||
return poolManager, nil
|
||||
}
|
||||
|
||||
func (r *Runner) InstallOrgWebhook(ctx context.Context, orgID string, param params.InstallWebhookParams) error {
|
||||
if !auth.IsAdmin(ctx) {
|
||||
return runnerErrors.ErrUnauthorized
|
||||
}
|
||||
|
||||
org, err := r.store.GetOrganizationByID(ctx, orgID)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "fetching org")
|
||||
}
|
||||
|
||||
poolMgr, err := r.poolManagerCtrl.GetOrgPoolManager(org)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "fetching pool manager for org")
|
||||
}
|
||||
|
||||
if err := poolMgr.InstallWebhook(ctx, param); err != nil {
|
||||
return errors.Wrap(err, "installing webhook")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Runner) UninstallOrgWebhook(ctx context.Context, orgID string) error {
|
||||
if !auth.IsAdmin(ctx) {
|
||||
return runnerErrors.ErrUnauthorized
|
||||
}
|
||||
|
||||
org, err := r.store.GetOrganizationByID(ctx, orgID)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "fetching org")
|
||||
}
|
||||
|
||||
poolMgr, err := r.poolManagerCtrl.GetOrgPoolManager(org)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "fetching pool manager for org")
|
||||
}
|
||||
|
||||
if err := poolMgr.UninstallWebhook(ctx); err != nil {
|
||||
return errors.Wrap(err, "uninstalling webhook")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -197,14 +197,6 @@ func (r *enterprise) WebhookSecret() string {
|
|||
return r.cfg.WebhookSecret
|
||||
}
|
||||
|
||||
func (r *enterprise) GetCallbackURL() string {
|
||||
return r.cfgInternal.InstanceCallbackURL
|
||||
}
|
||||
|
||||
func (r *enterprise) GetMetadataURL() string {
|
||||
return r.cfgInternal.InstanceMetadataURL
|
||||
}
|
||||
|
||||
func (r *enterprise) FindPoolByTags(labels []string) (params.Pool, error) {
|
||||
pool, err := r.store.FindEnterprisePoolByTags(r.ctx, r.id, labels)
|
||||
if err != nil {
|
||||
|
|
@ -231,3 +223,11 @@ func (r *enterprise) ValidateOwner(job params.WorkflowJob) error {
|
|||
func (r *enterprise) ID() string {
|
||||
return r.id
|
||||
}
|
||||
|
||||
func (r *enterprise) InstallHook(ctx context.Context, req *github.Hook) error {
|
||||
return fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (r *enterprise) UninstallHook(ctx context.Context, url string) error {
|
||||
return fmt.Errorf("not implemented")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
package pool
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cloudbase/garm/params"
|
||||
"github.com/cloudbase/garm/runner/common"
|
||||
|
||||
|
|
@ -29,6 +31,9 @@ type poolHelper interface {
|
|||
RemoveGithubRunner(runnerID int64) (*github.Response, error)
|
||||
FetchTools() ([]*github.RunnerApplicationDownload, error)
|
||||
|
||||
InstallHook(ctx context.Context, req *github.Hook) error
|
||||
UninstallHook(ctx context.Context, url string) error
|
||||
|
||||
GithubCLI() common.GithubClient
|
||||
|
||||
FetchDbInstances() ([]params.Instance, error)
|
||||
|
|
@ -36,8 +41,6 @@ type poolHelper interface {
|
|||
GithubURL() string
|
||||
JwtToken() string
|
||||
String() string
|
||||
GetCallbackURL() string
|
||||
GetMetadataURL() string
|
||||
FindPoolByTags(labels []string) (params.Pool, error)
|
||||
GetPoolByID(poolID string) (params.Pool, error)
|
||||
ValidateOwner(job params.WorkflowJob) error
|
||||
|
|
|
|||
|
|
@ -57,6 +57,12 @@ func NewOrganizationPoolManager(ctx context.Context, cfg params.Organization, cf
|
|||
store: store,
|
||||
providers: providers,
|
||||
controllerID: cfgInternal.ControllerID,
|
||||
urls: urls{
|
||||
webhookURL: cfgInternal.BaseWebhookURL,
|
||||
callbackURL: cfgInternal.InstanceCallbackURL,
|
||||
metadataURL: cfgInternal.InstanceMetadataURL,
|
||||
controllerWebhookURL: cfgInternal.ControllerWebhookURL,
|
||||
},
|
||||
quit: make(chan struct{}),
|
||||
helper: helper,
|
||||
credsDetails: cfgInternal.GithubCredentialsDetails,
|
||||
|
|
@ -210,14 +216,6 @@ func (r *organization) WebhookSecret() string {
|
|||
return r.cfg.WebhookSecret
|
||||
}
|
||||
|
||||
func (r *organization) GetCallbackURL() string {
|
||||
return r.cfgInternal.InstanceCallbackURL
|
||||
}
|
||||
|
||||
func (r *organization) GetMetadataURL() string {
|
||||
return r.cfgInternal.InstanceMetadataURL
|
||||
}
|
||||
|
||||
func (r *organization) FindPoolByTags(labels []string) (params.Pool, error) {
|
||||
pool, err := r.store.FindOrganizationPoolByTags(r.ctx, r.id, labels)
|
||||
if err != nil {
|
||||
|
|
@ -244,3 +242,59 @@ func (r *organization) ValidateOwner(job params.WorkflowJob) error {
|
|||
func (r *organization) ID() string {
|
||||
return r.id
|
||||
}
|
||||
|
||||
func (r *organization) listHooks(ctx context.Context) ([]*github.Hook, error) {
|
||||
opts := github.ListOptions{
|
||||
PerPage: 100,
|
||||
}
|
||||
var allHooks []*github.Hook
|
||||
for {
|
||||
hooks, ghResp, err := r.ghcli.ListOrgHooks(ctx, r.cfg.Name, &opts)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "fetching hooks")
|
||||
}
|
||||
allHooks = append(allHooks, hooks...)
|
||||
if ghResp.NextPage == 0 {
|
||||
break
|
||||
}
|
||||
opts.Page = ghResp.NextPage
|
||||
}
|
||||
return allHooks, nil
|
||||
}
|
||||
|
||||
func (r *organization) InstallHook(ctx context.Context, req *github.Hook) error {
|
||||
allHooks, err := r.listHooks(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "listing hooks")
|
||||
}
|
||||
|
||||
for _, hook := range allHooks {
|
||||
if hook.Config["url"] == req.Config["url"] {
|
||||
return fmt.Errorf("hook already installed: %w", runnerErrors.ErrBadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
_, _, err = r.ghcli.CreateOrgHook(ctx, r.cfg.Name, req)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "creating organization hook")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *organization) UninstallHook(ctx context.Context, url string) error {
|
||||
allHooks, err := r.listHooks(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "listing hooks")
|
||||
}
|
||||
|
||||
for _, hook := range allHooks {
|
||||
if hook.Config["url"] == url {
|
||||
_, err = r.ghcli.DeleteOrgHook(ctx, r.cfg.Name, hook.GetID())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "deleting hook")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,6 +85,12 @@ func (k *keyMutex) Delete(key string) {
|
|||
k.muxes.Delete(key)
|
||||
}
|
||||
|
||||
type urls struct {
|
||||
callbackURL string
|
||||
metadataURL string
|
||||
webhookURL string
|
||||
controllerWebhookURL string
|
||||
}
|
||||
type basePoolManager struct {
|
||||
ctx context.Context
|
||||
controllerID string
|
||||
|
|
@ -101,6 +107,8 @@ type basePoolManager struct {
|
|||
managerIsRunning bool
|
||||
managerErrorReason string
|
||||
|
||||
urls urls
|
||||
|
||||
mux sync.Mutex
|
||||
wg *sync.WaitGroup
|
||||
keyMux *keyMutex
|
||||
|
|
@ -686,8 +694,8 @@ func (r *basePoolManager) AddRunner(ctx context.Context, poolID string, aditiona
|
|||
RunnerStatus: params.RunnerPending,
|
||||
OSArch: pool.OSArch,
|
||||
OSType: pool.OSType,
|
||||
CallbackURL: r.helper.GetCallbackURL(),
|
||||
MetadataURL: r.helper.GetMetadataURL(),
|
||||
CallbackURL: r.urls.callbackURL,
|
||||
MetadataURL: r.urls.metadataURL,
|
||||
CreateAttempt: 1,
|
||||
GitHubRunnerGroup: pool.GitHubRunnerGroup,
|
||||
AditionalLabels: aditionalLabels,
|
||||
|
|
@ -1598,3 +1606,36 @@ func (r *basePoolManager) consumeQueuedJobs() error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *basePoolManager) InstallWebhook(ctx context.Context, param params.InstallWebhookParams) error {
|
||||
if r.urls.controllerWebhookURL == "" {
|
||||
return errors.Wrap(runnerErrors.ErrUnprocessable, "controller webhook url is empty")
|
||||
}
|
||||
|
||||
insecureSSL := "0"
|
||||
if param.InsecureSSL {
|
||||
insecureSSL = "1"
|
||||
}
|
||||
req := &github.Hook{
|
||||
Active: github.Bool(true),
|
||||
Config: map[string]interface{}{
|
||||
"url": r.urls.controllerWebhookURL,
|
||||
"content_type": "json",
|
||||
"insecure_ssl": insecureSSL,
|
||||
"secret": r.WebhookSecret(),
|
||||
},
|
||||
Events: []string{
|
||||
"workflow_job",
|
||||
},
|
||||
}
|
||||
|
||||
return r.helper.InstallHook(ctx, req)
|
||||
}
|
||||
|
||||
func (r *basePoolManager) UninstallWebhook(ctx context.Context) error {
|
||||
if r.urls.controllerWebhookURL == "" {
|
||||
return errors.Wrap(runnerErrors.ErrUnprocessable, "controller webhook url is empty")
|
||||
}
|
||||
|
||||
return r.helper.UninstallHook(ctx, r.urls.controllerWebhookURL)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -211,14 +211,6 @@ func (r *repository) WebhookSecret() string {
|
|||
return r.cfg.WebhookSecret
|
||||
}
|
||||
|
||||
func (r *repository) GetCallbackURL() string {
|
||||
return r.cfgInternal.InstanceCallbackURL
|
||||
}
|
||||
|
||||
func (r *repository) GetMetadataURL() string {
|
||||
return r.cfgInternal.InstanceMetadataURL
|
||||
}
|
||||
|
||||
func (r *repository) FindPoolByTags(labels []string) (params.Pool, error) {
|
||||
pool, err := r.store.FindRepositoryPoolByTags(r.ctx, r.id, labels)
|
||||
if err != nil {
|
||||
|
|
@ -245,3 +237,59 @@ func (r *repository) ValidateOwner(job params.WorkflowJob) error {
|
|||
func (r *repository) ID() string {
|
||||
return r.id
|
||||
}
|
||||
|
||||
func (r *repository) listHooks(ctx context.Context) ([]*github.Hook, error) {
|
||||
opts := github.ListOptions{
|
||||
PerPage: 100,
|
||||
}
|
||||
var allHooks []*github.Hook
|
||||
for {
|
||||
hooks, ghResp, err := r.ghcli.ListRepoHooks(ctx, r.cfg.Owner, r.cfg.Name, &opts)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "fetching hooks")
|
||||
}
|
||||
allHooks = append(allHooks, hooks...)
|
||||
if ghResp.NextPage == 0 {
|
||||
break
|
||||
}
|
||||
opts.Page = ghResp.NextPage
|
||||
}
|
||||
return allHooks, nil
|
||||
}
|
||||
|
||||
func (r *repository) InstallHook(ctx context.Context, req *github.Hook) error {
|
||||
allHooks, err := r.listHooks(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "listing hooks")
|
||||
}
|
||||
|
||||
for _, hook := range allHooks {
|
||||
if hook.Config["url"] == req.Config["url"] {
|
||||
return fmt.Errorf("hook already installed: %w", runnerErrors.ErrBadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
_, _, err = r.ghcli.CreateRepoHook(ctx, r.cfg.Owner, r.cfg.Name, req)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "creating repository hook")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *repository) UninstallHook(ctx context.Context, url string) error {
|
||||
allHooks, err := r.listHooks(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "listing hooks")
|
||||
}
|
||||
|
||||
for _, hook := range allHooks {
|
||||
if hook.Config["url"] == url {
|
||||
_, err = r.ghcli.DeleteRepoHook(ctx, r.cfg.Owner, r.cfg.Name, *hook.ID)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "deleting hook")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -349,3 +349,45 @@ func (r *Runner) findRepoPoolManager(owner, name string) (common.PoolManager, er
|
|||
}
|
||||
return poolManager, nil
|
||||
}
|
||||
|
||||
func (r *Runner) InstallRepoWebhook(ctx context.Context, repoID string, param params.InstallWebhookParams) error {
|
||||
if !auth.IsAdmin(ctx) {
|
||||
return runnerErrors.ErrUnauthorized
|
||||
}
|
||||
|
||||
repo, err := r.store.GetRepositoryByID(ctx, repoID)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "fetching repo")
|
||||
}
|
||||
|
||||
poolManager, err := r.poolManagerCtrl.GetRepoPoolManager(repo)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "fetching pool manager for repo")
|
||||
}
|
||||
|
||||
if err := poolManager.InstallWebhook(ctx, param); err != nil {
|
||||
return errors.Wrap(err, "installing webhook")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Runner) UninstallRepoWebhook(ctx context.Context, repoID string) error {
|
||||
if !auth.IsAdmin(ctx) {
|
||||
return runnerErrors.ErrUnauthorized
|
||||
}
|
||||
|
||||
repo, err := r.store.GetRepositoryByID(ctx, repoID)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "fetching repo")
|
||||
}
|
||||
|
||||
poolManager, err := r.poolManagerCtrl.GetRepoPoolManager(repo)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "fetching pool manager for repo")
|
||||
}
|
||||
|
||||
if err := poolManager.UninstallWebhook(ctx); err != nil {
|
||||
return errors.Wrap(err, "uninstalling webhook")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -311,12 +311,18 @@ func (p *poolManagerCtrl) getInternalConfig(credsName string) (params.Internal,
|
|||
return params.Internal{}, fmt.Errorf("fetching CA bundle for creds: %w", err)
|
||||
}
|
||||
|
||||
var controllerWebhookURL string
|
||||
if p.config.Default.WebhookURL != "" {
|
||||
controllerWebhookURL = fmt.Sprintf("%s/%s", p.config.Default.WebhookURL, p.controllerID)
|
||||
}
|
||||
return params.Internal{
|
||||
OAuth2Token: creds.OAuth2Token,
|
||||
ControllerID: p.controllerID,
|
||||
InstanceCallbackURL: p.config.Default.CallbackURL,
|
||||
InstanceMetadataURL: p.config.Default.MetadataURL,
|
||||
JWTSecret: p.config.JWTAuth.Secret,
|
||||
OAuth2Token: creds.OAuth2Token,
|
||||
ControllerID: p.controllerID,
|
||||
InstanceCallbackURL: p.config.Default.CallbackURL,
|
||||
InstanceMetadataURL: p.config.Default.MetadataURL,
|
||||
BaseWebhookURL: p.config.Default.WebhookURL,
|
||||
ControllerWebhookURL: controllerWebhookURL,
|
||||
JWTSecret: p.config.JWTAuth.Secret,
|
||||
GithubCredentialsDetails: params.GithubCredentials{
|
||||
Name: creds.Name,
|
||||
Description: creds.Description,
|
||||
|
|
@ -376,11 +382,17 @@ func (r *Runner) GetControllerInfo(ctx context.Context) (params.ControllerInfo,
|
|||
return params.ControllerInfo{}, errors.Wrap(err, "fetching hostname")
|
||||
}
|
||||
r.controllerInfo.Hostname = hostname
|
||||
var controllerWebhook string
|
||||
if r.controllerID != uuid.Nil && r.config.Default.WebhookURL != "" {
|
||||
controllerWebhook = fmt.Sprintf("%s/%s", r.config.Default.WebhookURL, r.controllerID.String())
|
||||
}
|
||||
return params.ControllerInfo{
|
||||
ControllerID: r.controllerID,
|
||||
Hostname: hostname,
|
||||
MetadataURL: r.config.Default.MetadataURL,
|
||||
CallbackURL: r.config.Default.CallbackURL,
|
||||
ControllerID: r.controllerID,
|
||||
Hostname: hostname,
|
||||
MetadataURL: r.config.Default.MetadataURL,
|
||||
CallbackURL: r.config.Default.CallbackURL,
|
||||
WebhookURL: r.config.Default.WebhookURL,
|
||||
ControllerWebhookURL: controllerWebhook,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
2
testdata/config.toml
vendored
2
testdata/config.toml
vendored
|
|
@ -17,6 +17,8 @@ callback_url = "https://garm.example.com/api/v1/callbacks"
|
|||
# highly encouraged.
|
||||
metadata_url = "https://garm.example.com/api/v1/metadata"
|
||||
|
||||
webhook_url = "https://garm.example.com/webhooks"
|
||||
|
||||
# Uncomment this line if you'd like to log to a file instead of standard output.
|
||||
# log_file = "/tmp/runner-manager.log"
|
||||
|
||||
|
|
|
|||
45
util/util.go
45
util/util.go
|
|
@ -29,6 +29,44 @@ import (
|
|||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
type githubClient struct {
|
||||
*github.ActionsService
|
||||
org *github.OrganizationsService
|
||||
repo *github.RepositoriesService
|
||||
}
|
||||
|
||||
func (g *githubClient) ListOrgHooks(ctx context.Context, org string, opts *github.ListOptions) ([]*github.Hook, *github.Response, error) {
|
||||
return g.org.ListHooks(ctx, org, opts)
|
||||
}
|
||||
|
||||
func (g *githubClient) GetOrgHook(ctx context.Context, org string, id int64) (*github.Hook, *github.Response, error) {
|
||||
return g.org.GetHook(ctx, org, id)
|
||||
}
|
||||
|
||||
func (g *githubClient) CreateOrgHook(ctx context.Context, org string, hook *github.Hook) (*github.Hook, *github.Response, error) {
|
||||
return g.org.CreateHook(ctx, org, hook)
|
||||
}
|
||||
|
||||
func (g *githubClient) DeleteOrgHook(ctx context.Context, org string, id int64) (*github.Response, error) {
|
||||
return g.org.DeleteHook(ctx, org, id)
|
||||
}
|
||||
|
||||
func (g *githubClient) ListRepoHooks(ctx context.Context, owner, repo string, opts *github.ListOptions) ([]*github.Hook, *github.Response, error) {
|
||||
return g.repo.ListHooks(ctx, owner, repo, opts)
|
||||
}
|
||||
|
||||
func (g *githubClient) GetRepoHook(ctx context.Context, owner, repo string, id int64) (*github.Hook, *github.Response, error) {
|
||||
return g.repo.GetHook(ctx, owner, repo, id)
|
||||
}
|
||||
|
||||
func (g *githubClient) CreateRepoHook(ctx context.Context, owner, repo string, hook *github.Hook) (*github.Hook, *github.Response, error) {
|
||||
return g.repo.CreateHook(ctx, owner, repo, hook)
|
||||
}
|
||||
|
||||
func (g *githubClient) DeleteRepoHook(ctx context.Context, owner, repo string, id int64) (*github.Response, error) {
|
||||
return g.repo.DeleteHook(ctx, owner, repo, id)
|
||||
}
|
||||
|
||||
func GithubClient(ctx context.Context, token string, credsDetails params.GithubCredentials) (common.GithubClient, common.GithubEnterpriseClient, error) {
|
||||
var roots *x509.CertPool
|
||||
if credsDetails.CABundle != nil && len(credsDetails.CABundle) > 0 {
|
||||
|
|
@ -56,5 +94,10 @@ func GithubClient(ctx context.Context, token string, credsDetails params.GithubC
|
|||
return nil, nil, errors.Wrap(err, "fetching github client")
|
||||
}
|
||||
|
||||
return ghClient.Actions, ghClient.Enterprise, nil
|
||||
cli := &githubClient{
|
||||
ActionsService: ghClient.Actions,
|
||||
org: ghClient.Organizations,
|
||||
repo: ghClient.Repositories,
|
||||
}
|
||||
return cli, ghClient.Enterprise, nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue