diff --git a/pkg/operators/provisioning/config.go b/pkg/operators/provisioning/config.go index 5b3c22bab97..dd2d8a73068 100644 --- a/pkg/operators/provisioning/config.go +++ b/pkg/operators/provisioning/config.go @@ -26,6 +26,7 @@ import ( "github.com/grafana/grafana/apps/provisioning/pkg/repository/github" "github.com/grafana/grafana/apps/provisioning/pkg/repository/local" "github.com/grafana/grafana/pkg/registry/apis/provisioning/resources" + "github.com/grafana/grafana/pkg/registry/apis/provisioning/webhooks" secretdecrypt "github.com/grafana/grafana/pkg/registry/apis/secret/decrypt" ) @@ -55,6 +56,7 @@ type provisioningControllerConfig struct { // audiences = // [operator] // provisioning_server_url = +// provisioning_server_public_url = // dashboards_server_url = // folders_server_url = // tls_insecure = @@ -233,12 +235,16 @@ func setupRepoFactory( switch provisioning.RepositoryType(t) { case provisioning.GitHubRepositoryType: + var webhook *webhooks.WebhookExtraBuilder + provisioningAppURL := operatorSec.Key("provisioning_server_public_url").String() + if provisioningAppURL != "" { + webhook = webhooks.ProvideWebhooks(provisioningAppURL) + } + extras = append(extras, github.Extra( decrypter, github.ProvideFactory(), - // TODO: we need to plug the webhook builder here for webhooks to be created in repository controller - // https://github.com/grafana/git-ui-sync-project/issues/455 - nil, + webhook, ), ) case provisioning.LocalRepositoryType: diff --git a/pkg/registry/apis/provisioning/extra.go b/pkg/registry/apis/provisioning/extra.go index af642a7216c..d549b6d102d 100644 --- a/pkg/registry/apis/provisioning/extra.go +++ b/pkg/registry/apis/provisioning/extra.go @@ -3,7 +3,6 @@ package provisioning import ( "context" - "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" "k8s.io/apiserver/pkg/authorization/authorizer" "k8s.io/apiserver/pkg/registry/rest" "k8s.io/kube-openapi/pkg/spec3" @@ -13,7 +12,6 @@ type Extra interface { Authorize(ctx context.Context, a authorizer.Attributes) (decision authorizer.Decision, reason string, err error) UpdateStorage(storage map[string]rest.Storage) error PostProcessOpenAPI(oas *spec3.OpenAPI) error - GetJobWorkers() []jobs.Worker } type ExtraBuilder func(b *APIBuilder) Extra diff --git a/pkg/registry/apis/provisioning/extras/register.go b/pkg/registry/apis/provisioning/extras/register.go index 7800f41b6b2..16d4ef2da12 100644 --- a/pkg/registry/apis/provisioning/extras/register.go +++ b/pkg/registry/apis/provisioning/extras/register.go @@ -6,12 +6,14 @@ import ( "github.com/grafana/grafana/apps/provisioning/pkg/repository/local" "github.com/grafana/grafana/apps/secret/pkg/decrypt" "github.com/grafana/grafana/pkg/registry/apis/provisioning" + "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" "github.com/grafana/grafana/pkg/registry/apis/provisioning/webhooks" + "github.com/grafana/grafana/pkg/registry/apis/provisioning/webhooks/pullrequest" "github.com/grafana/grafana/pkg/setting" ) // HACK: This is a hack so that wire can uniquely identify dependencies -func ProvideProvisioningOSSExtras(webhook *webhooks.WebhookExtraBuilder) []provisioning.ExtraBuilder { +func ProvideProvisioningExtraAPIs(webhook *webhooks.WebhookExtraBuilder) []provisioning.ExtraBuilder { return []provisioning.ExtraBuilder{ webhook.ExtraBuilder, } @@ -35,3 +37,7 @@ func ProvideProvisioningOSSRepositoryExtras( ), } } + +func ProvideExtraWorkers(pullRequestWorker *pullrequest.PullRequestWorker) []jobs.Worker { + return []jobs.Worker{pullRequestWorker} +} diff --git a/pkg/registry/apis/provisioning/register.go b/pkg/registry/apis/provisioning/register.go index 5162ccd2b31..3a325b67d95 100644 --- a/pkg/registry/apis/provisioning/register.go +++ b/pkg/registry/apis/provisioning/register.go @@ -108,7 +108,8 @@ type APIBuilder struct { statusPatcher *appcontroller.RepositoryStatusPatcher healthChecker *controller.HealthChecker // Extras provides additional functionality to the API. - extras []Extra + extras []Extra + extraWorkers []jobs.Worker } // NewAPIBuilder creates an API builder. @@ -126,6 +127,7 @@ func NewAPIBuilder( access authlib.AccessChecker, tracer tracing.Tracer, extraBuilders []ExtraBuilder, + extraWorkers []jobs.Worker, jobHistoryConfig *JobHistoryConfig, ) *APIBuilder { clients := resources.NewClientFactory(configProvider) @@ -147,6 +149,7 @@ func NewAPIBuilder( unified: unified, access: access, jobHistoryConfig: jobHistoryConfig, + extraWorkers: extraWorkers, } for _, builder := range extraBuilders { @@ -204,6 +207,7 @@ func RegisterAPIService( usageStats usagestats.Service, tracer tracing.Tracer, extraBuilders []ExtraBuilder, + extraWorkers []jobs.Worker, repoFactory repository.Factory, ) (*APIBuilder, error) { if !features.IsEnabledGlobally(featuremgmt.FlagProvisioning) { @@ -221,6 +225,7 @@ func RegisterAPIService( access, tracer, extraBuilders, + extraWorkers, createJobHistoryConfigFromSettings(cfg), ) apiregistration.RegisterAPI(builder) @@ -720,9 +725,7 @@ func (b *APIBuilder) GetPostStartHooks() (map[string]genericapiserver.PostStartH } // Add any extra workers - for _, extra := range b.extras { - workers = append(workers, extra.GetJobWorkers()...) - } + workers = append(workers, b.extraWorkers...) var jobHistoryWriter jobs.HistoryWriter if b.jobHistoryLoki != nil { diff --git a/pkg/registry/apis/provisioning/webhooks/pullrequest/render.go b/pkg/registry/apis/provisioning/webhooks/pullrequest/render.go index a6de6435c7a..f1ecfdb7f73 100644 --- a/pkg/registry/apis/provisioning/webhooks/pullrequest/render.go +++ b/pkg/registry/apis/provisioning/webhooks/pullrequest/render.go @@ -106,3 +106,19 @@ func (r *screenshotRenderer) RenderScreenshot(ctx context.Context, repo provisio return fmt.Sprintf("apis/%s/namespaces/%s/repositories/%s/render/%s", provisioning.APIVERSION, repo.Namespace, repo.Name, rsp.Uid), nil } + +type NoOpRenderer struct{} + +func NewNoOpRenderer() ScreenshotRenderer { + return &NoOpRenderer{} +} + +func (r *NoOpRenderer) IsAvailable(_ context.Context) bool { + return false +} + +func (r *NoOpRenderer) RenderScreenshot( + _ context.Context, _ provisioning.ResourceRepositoryInfo, _ string, _ url.Values, +) (string, error) { + return "", nil +} diff --git a/pkg/registry/apis/provisioning/webhooks/pullrequest/worker.go b/pkg/registry/apis/provisioning/webhooks/pullrequest/worker.go index 1c9d329cccd..1291c590148 100644 --- a/pkg/registry/apis/provisioning/webhooks/pullrequest/worker.go +++ b/pkg/registry/apis/provisioning/webhooks/pullrequest/worker.go @@ -12,8 +12,33 @@ import ( "github.com/grafana/grafana/apps/provisioning/pkg/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" "github.com/grafana/grafana/pkg/registry/apis/provisioning/resources" + "github.com/grafana/grafana/pkg/services/apiserver" + "github.com/grafana/grafana/pkg/services/rendering" + "github.com/grafana/grafana/pkg/setting" + "github.com/grafana/grafana/pkg/storage/unified/resource" ) +func ProvidePullRequestWorker( + cfg *setting.Cfg, + renderer rendering.Service, + blobstore resource.ResourceClient, + configProvider apiserver.RestConfigProvider, +) *PullRequestWorker { + urlProvider := func(_ string) string { + return cfg.AppURL + } + + // FIXME: we should create providers for client and parsers, so that we don't have + // multiple connections for webhooks + clients := resources.NewClientFactory(configProvider) + parsers := resources.NewParserFactory(clients) + screenshotRenderer := NewScreenshotRenderer(renderer, blobstore) + evaluator := NewEvaluator(screenshotRenderer, parsers, urlProvider) + commenter := NewCommenter() + + return NewPullRequestWorker(evaluator, commenter) +} + //go:generate mockery --name=PullRequestRepo --structname=MockPullRequestRepo --inpackage --filename=mock_pullrequest_repo.go --with-expecter type PullRequestRepo interface { Config() *provisioning.Repository diff --git a/pkg/registry/apis/provisioning/webhooks/register.go b/pkg/registry/apis/provisioning/webhooks/register.go index a26b6327d5a..ce357188bad 100644 --- a/pkg/registry/apis/provisioning/webhooks/register.go +++ b/pkg/registry/apis/provisioning/webhooks/register.go @@ -28,6 +28,7 @@ type WebhookExtraBuilder struct { urlProvider func(namespace string) string } +// FIXME: separate the URL provider from connector to simplify operators func (b *WebhookExtraBuilder) WebhookURL(ctx context.Context, r *provisioning.Repository) string { if !b.isPublic { return "" @@ -57,7 +58,7 @@ func isPublicURL(url string) bool { !strings.HasPrefix(url, "https://172.16.") } -func ProvideWebhooks( +func ProvideWebhooksWithImages( cfg *setting.Cfg, renderer rendering.Service, blobstore resource.ResourceClient, @@ -87,7 +88,7 @@ func ProvideWebhooks( commenter := pullrequest.NewCommenter() pullRequestWorker := pullrequest.NewPullRequestWorker(evaluator, commenter) - return NewWebhookExtra( + return NewWebhookExtraWithImages( render, webhook, urlProvider, @@ -97,21 +98,40 @@ func ProvideWebhooks( } } -// WebhookExtra implements the Extra interface for webhooks +func ProvideWebhooks(provisioningURL string) *WebhookExtraBuilder { + urlProvider := func(_ string) string { + return provisioningURL + } + + isPublic := isPublicURL(urlProvider("")) + + return &WebhookExtraBuilder{ + isPublic: isPublic, + urlProvider: urlProvider, + ExtraBuilder: func(b *provisioningapis.APIBuilder) provisioningapis.Extra { + screenshotRenderer := pullrequest.NewNoOpRenderer() + webhook := NewWebhookConnector(isPublic, b, screenshotRenderer) + + return NewWebhookExtra(webhook) + }, + } +} + +// WebhookExtraWithImages implements the Extra interface for webhooks // to wrap around -type WebhookExtra struct { +type WebhookExtraWithImages struct { render *renderConnector webhook *webhookConnector workers []jobs.Worker } -func NewWebhookExtra( +func NewWebhookExtraWithImages( render *renderConnector, webhook *webhookConnector, urlProvider func(namespace string) string, workers []jobs.Worker, -) *WebhookExtra { - return &WebhookExtra{ +) *WebhookExtraWithImages { + return &WebhookExtraWithImages{ render: render, webhook: webhook, workers: workers, @@ -119,7 +139,7 @@ func NewWebhookExtra( } // Authorize delegates authorization to the webhook connector -func (e *WebhookExtra) Authorize(ctx context.Context, a authorizer.Attributes) (decision authorizer.Decision, reason string, err error) { +func (e *WebhookExtraWithImages) Authorize(ctx context.Context, a authorizer.Attributes) (decision authorizer.Decision, reason string, err error) { webhookDecision, webhookReason, webhookErr := e.webhook.Authorize(ctx, a) if webhookDecision != authorizer.DecisionNoOpinion { return webhookDecision, webhookReason, webhookErr @@ -129,7 +149,7 @@ func (e *WebhookExtra) Authorize(ctx context.Context, a authorizer.Attributes) ( } // UpdateStorage updates the storage with both render and webhook connectors -func (e *WebhookExtra) UpdateStorage(storage map[string]rest.Storage) error { +func (e *WebhookExtraWithImages) UpdateStorage(storage map[string]rest.Storage) error { if err := e.webhook.UpdateStorage(storage); err != nil { return err } @@ -138,7 +158,7 @@ func (e *WebhookExtra) UpdateStorage(storage map[string]rest.Storage) error { } // PostProcessOpenAPI processes OpenAPI specs for both connectors -func (e *WebhookExtra) PostProcessOpenAPI(oas *spec3.OpenAPI) error { +func (e *WebhookExtraWithImages) PostProcessOpenAPI(oas *spec3.OpenAPI) error { if err := e.webhook.PostProcessOpenAPI(oas); err != nil { return err } @@ -146,7 +166,25 @@ func (e *WebhookExtra) PostProcessOpenAPI(oas *spec3.OpenAPI) error { return e.render.PostProcessOpenAPI(oas) } -// GetJobWorkers returns job workers from the webhook connector -func (e *WebhookExtra) GetJobWorkers() []jobs.Worker { - return e.workers +type WebhookExtra struct { + webhook *webhookConnector +} + +func NewWebhookExtra(webhook *webhookConnector) *WebhookExtra { + return &WebhookExtra{webhook: webhook} +} + +// Authorize delegates authorization to the webhook connector +func (e *WebhookExtra) Authorize(ctx context.Context, a authorizer.Attributes) (decision authorizer.Decision, reason string, err error) { + return e.webhook.Authorize(ctx, a) +} + +// UpdateStorage updates the storage with webhook connector +func (e *WebhookExtra) UpdateStorage(storage map[string]rest.Storage) error { + return e.webhook.UpdateStorage(storage) +} + +// PostProcessOpenAPI processes OpenAPI specs for webhook connectors +func (e *WebhookExtra) PostProcessOpenAPI(oas *spec3.OpenAPI) error { + return e.webhook.PostProcessOpenAPI(oas) } diff --git a/pkg/registry/apis/wireset.go b/pkg/registry/apis/wireset.go index fa79abd8977..6027335a0b7 100644 --- a/pkg/registry/apis/wireset.go +++ b/pkg/registry/apis/wireset.go @@ -3,6 +3,7 @@ package apiregistry import ( "github.com/google/wire" + "github.com/grafana/grafana/apps/provisioning/pkg/repository" dashboardinternal "github.com/grafana/grafana/pkg/registry/apis/dashboard" "github.com/grafana/grafana/pkg/registry/apis/dashboardsnapshot" "github.com/grafana/grafana/pkg/registry/apis/datasource" @@ -13,6 +14,9 @@ import ( "github.com/grafana/grafana/pkg/registry/apis/ofrep" "github.com/grafana/grafana/pkg/registry/apis/preferences" "github.com/grafana/grafana/pkg/registry/apis/provisioning" + "github.com/grafana/grafana/pkg/registry/apis/provisioning/extras" + "github.com/grafana/grafana/pkg/registry/apis/provisioning/webhooks" + "github.com/grafana/grafana/pkg/registry/apis/provisioning/webhooks/pullrequest" "github.com/grafana/grafana/pkg/registry/apis/query" "github.com/grafana/grafana/pkg/registry/apis/secret" "github.com/grafana/grafana/pkg/registry/apis/service" @@ -27,6 +31,14 @@ var WireSetExts = wire.NewSet( wire.Bind(new(iam.RoleStorageBackend), new(*noopstorage.StorageBackendImpl)), ) +var provisioningExtras = wire.NewSet( + repository.ProvideFactory, + pullrequest.ProvidePullRequestWorker, + webhooks.ProvideWebhooksWithImages, + extras.ProvideProvisioningExtraAPIs, + extras.ProvideExtraWorkers, +) + var WireSet = wire.NewSet( ProvideRegistryServiceSink, // dummy background service that forces registration @@ -37,6 +49,8 @@ var WireSet = wire.NewSet( // Secrets secret.RegisterDependencies, + // Provisioning + provisioningExtras, // Each must be added here *and* in the ServiceSink above dashboardinternal.RegisterAPIService, diff --git a/pkg/server/wire_gen.go b/pkg/server/wire_gen.go index 7eb34a312ef..4eb6578a10b 100644 --- a/pkg/server/wire_gen.go +++ b/pkg/server/wire_gen.go @@ -63,6 +63,7 @@ import ( provisioning2 "github.com/grafana/grafana/pkg/registry/apis/provisioning" "github.com/grafana/grafana/pkg/registry/apis/provisioning/extras" "github.com/grafana/grafana/pkg/registry/apis/provisioning/webhooks" + "github.com/grafana/grafana/pkg/registry/apis/provisioning/webhooks/pullrequest" query2 "github.com/grafana/grafana/pkg/registry/apis/query" "github.com/grafana/grafana/pkg/registry/apis/secret" "github.com/grafana/grafana/pkg/registry/apis/secret/clock" @@ -814,8 +815,10 @@ func Initialize(ctx context.Context, cfg *setting.Cfg, opts Options, apiOpts api userStorageAPIBuilder := userstorage.RegisterAPIService(featureToggles, apiserverService, registerer) apiBuilder := preferences.RegisterAPIService(cfg, featureToggles, sqlStore, prefService, starService, userService, apiserverService) legacyMigrator := legacy.ProvideLegacyMigrator(sqlStore, provisioningServiceImpl, libraryPanelService, dashboardPermissionsService, accessControl, featureToggles) - webhookExtraBuilder := webhooks.ProvideWebhooks(cfg, renderingService, resourceClient, eventualRestConfigProvider) - v3 := extras.ProvideProvisioningOSSExtras(webhookExtraBuilder) + webhookExtraBuilder := webhooks.ProvideWebhooksWithImages(cfg, renderingService, resourceClient, eventualRestConfigProvider) + v3 := extras.ProvideProvisioningExtraAPIs(webhookExtraBuilder) + pullRequestWorker := pullrequest.ProvidePullRequestWorker(cfg, renderingService, resourceClient, eventualRestConfigProvider) + v4 := extras.ProvideExtraWorkers(pullRequestWorker) decryptAuthorizer := decrypt.ProvideDecryptAuthorizer(tracer) decryptStorage, err := metadata.ProvideDecryptStorage(tracer, ossKeeperService, keeperMetadataStorage, secureValueMetadataStorage, decryptAuthorizer, registerer) if err != nil { @@ -826,12 +829,12 @@ func Initialize(ctx context.Context, cfg *setting.Cfg, opts Options, apiOpts api return nil, err } factory := github.ProvideFactory() - v4 := extras.ProvideProvisioningOSSRepositoryExtras(cfg, decryptService, factory, webhookExtraBuilder) - repositoryFactory, err := repository.ProvideFactory(v4) + v5 := extras.ProvideProvisioningOSSRepositoryExtras(cfg, decryptService, factory, webhookExtraBuilder) + repositoryFactory, err := repository.ProvideFactory(v5) if err != nil { return nil, err } - provisioningAPIBuilder, err := provisioning2.RegisterAPIService(cfg, featureToggles, apiserverService, registerer, resourceClient, eventualRestConfigProvider, accessClient, legacyMigrator, dualwriteService, usageStats, tracingService, v3, repositoryFactory) + provisioningAPIBuilder, err := provisioning2.RegisterAPIService(cfg, featureToggles, apiserverService, registerer, resourceClient, eventualRestConfigProvider, accessClient, legacyMigrator, dualwriteService, usageStats, tracingService, v3, v4, repositoryFactory) if err != nil { return nil, err } @@ -1401,8 +1404,10 @@ func InitializeForTest(ctx context.Context, t sqlutil.ITestDB, testingT interfac userStorageAPIBuilder := userstorage.RegisterAPIService(featureToggles, apiserverService, registerer) apiBuilder := preferences.RegisterAPIService(cfg, featureToggles, sqlStore, prefService, starService, userService, apiserverService) legacyMigrator := legacy.ProvideLegacyMigrator(sqlStore, provisioningServiceImpl, libraryPanelService, dashboardPermissionsService, accessControl, featureToggles) - webhookExtraBuilder := webhooks.ProvideWebhooks(cfg, renderingService, resourceClient, eventualRestConfigProvider) - v3 := extras.ProvideProvisioningOSSExtras(webhookExtraBuilder) + webhookExtraBuilder := webhooks.ProvideWebhooksWithImages(cfg, renderingService, resourceClient, eventualRestConfigProvider) + v3 := extras.ProvideProvisioningExtraAPIs(webhookExtraBuilder) + pullRequestWorker := pullrequest.ProvidePullRequestWorker(cfg, renderingService, resourceClient, eventualRestConfigProvider) + v4 := extras.ProvideExtraWorkers(pullRequestWorker) decryptAuthorizer := decrypt.ProvideDecryptAuthorizer(tracer) decryptStorage, err := metadata.ProvideDecryptStorage(tracer, ossKeeperService, keeperMetadataStorage, secureValueMetadataStorage, decryptAuthorizer, registerer) if err != nil { @@ -1413,12 +1418,12 @@ func InitializeForTest(ctx context.Context, t sqlutil.ITestDB, testingT interfac return nil, err } factory := github.ProvideFactory() - v4 := extras.ProvideProvisioningOSSRepositoryExtras(cfg, decryptService, factory, webhookExtraBuilder) - repositoryFactory, err := repository.ProvideFactory(v4) + v5 := extras.ProvideProvisioningOSSRepositoryExtras(cfg, decryptService, factory, webhookExtraBuilder) + repositoryFactory, err := repository.ProvideFactory(v5) if err != nil { return nil, err } - provisioningAPIBuilder, err := provisioning2.RegisterAPIService(cfg, featureToggles, apiserverService, registerer, resourceClient, eventualRestConfigProvider, accessClient, legacyMigrator, dualwriteService, usageStats, tracingService, v3, repositoryFactory) + provisioningAPIBuilder, err := provisioning2.RegisterAPIService(cfg, featureToggles, apiserverService, registerer, resourceClient, eventualRestConfigProvider, accessClient, legacyMigrator, dualwriteService, usageStats, tracingService, v3, v4, repositoryFactory) if err != nil { return nil, err } diff --git a/pkg/server/wireexts_oss.go b/pkg/server/wireexts_oss.go index 55c8c2256e0..5ba3b26347f 100644 --- a/pkg/server/wireexts_oss.go +++ b/pkg/server/wireexts_oss.go @@ -7,7 +7,6 @@ package server import ( "github.com/google/wire" - "github.com/grafana/grafana/apps/provisioning/pkg/repository" "github.com/grafana/grafana/pkg/configprovider" "github.com/grafana/grafana/pkg/infra/metrics" "github.com/grafana/grafana/pkg/infra/tracing" @@ -16,7 +15,6 @@ import ( "github.com/grafana/grafana/pkg/registry" apisregistry "github.com/grafana/grafana/pkg/registry/apis" "github.com/grafana/grafana/pkg/registry/apis/provisioning/extras" - "github.com/grafana/grafana/pkg/registry/apis/provisioning/webhooks" "github.com/grafana/grafana/pkg/registry/apis/secret" "github.com/grafana/grafana/pkg/registry/apis/secret/contracts" gsmKMSProviders "github.com/grafana/grafana/pkg/registry/apis/secret/encryption/kmsproviders" @@ -69,9 +67,6 @@ import ( ) var provisioningExtras = wire.NewSet( - webhooks.ProvideWebhooks, - repository.ProvideFactory, - extras.ProvideProvisioningOSSExtras, extras.ProvideProvisioningOSSRepositoryExtras, )