Skip to content

Commit

Permalink
Revert "Move repository create to RepositoryService (#2632)" (#2645)
Browse files Browse the repository at this point in the history
This reverts commit d68e8e6.
  • Loading branch information
JAORMX authored Mar 14, 2024
1 parent 8035c66 commit a0d83dc
Show file tree
Hide file tree
Showing 7 changed files with 372 additions and 483 deletions.
86 changes: 86 additions & 0 deletions internal/controlplane/handlers_githubwebhooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,92 @@ func handleParseError(typ string, parseErr error) *metrics.WebhookEventState {
return state
}

// registerWebhookForRepository registers a set repository and sets up the webhook for each of them
// and returns the registration result for each repository.
// If an error occurs, the registration is aborted and the error is returned.
// https://docs.github.com/en/rest/reference/repos#create-a-repository-webhook

// The actual logic for webhook creation lives in the WebhookManager interface
// TODO: the remaining logic should be refactored into a repository
// registration interface
func (s *Server) registerWebhookForRepository(
ctx context.Context,
pbuild *providers.ProviderBuilder,
projectID uuid.UUID,
repo *pb.UpstreamRepositoryRef,
) (*pb.RegisterRepoResult, error) {
logger := zerolog.Ctx(ctx).With().
Str("repoName", repo.Name).
Str("repoOwner", repo.Owner).
Logger()
ctx = logger.WithContext(ctx)

if !pbuild.Implements(db.ProviderTypeGithub) {
return nil, fmt.Errorf("provider %s is not supported for github webhook", pbuild.GetName())
}

client, err := pbuild.GetGitHub()
if err != nil {
return nil, fmt.Errorf("error creating github provider: %w", err)
}

regResult := &pb.RegisterRepoResult{
// We will overwrite this later when we've looked it up from the provider,
// but existing clients expect a message here, so let's add one.
Repository: &pb.Repository{
Name: repo.Name, // Not normalized, from client
Owner: repo.Owner, // Not normalized, from client
},
Status: &pb.RegisterRepoResult_Status{
Success: false,
},
}

// let's verify that the repository actually exists.
repoGet, err := client.GetRepository(ctx, repo.Owner, repo.Name)
if err != nil {
errorStr := err.Error()
regResult.Status.Error = &errorStr
return regResult, nil
}

// skip if we try to register a private repository
if repoGet.GetPrivate() && !features.ProjectAllowsPrivateRepos(ctx, s.store, projectID) {
errorStr := "repository is private"
regResult.Status.Error = &errorStr
return regResult, nil
}

hookUUID, githubHook, err := s.webhookManager.CreateWebhook(ctx, client, repo.Owner, repo.Name)
if err != nil {
logger.Error().Msgf("error while creating webhook: %v", err)
errorStr := err.Error()
regResult.Status.Error = &errorStr
return regResult, nil
}

regResult.Status.Success = true

regResult.Repository = &pb.Repository{
Name: repoGet.GetName(),
Owner: repoGet.GetOwner().GetLogin(),
RepoId: repoGet.GetID(),
HookId: githubHook.GetID(),
HookUrl: githubHook.GetURL(),
DeployUrl: repoGet.GetDeploymentsURL(),
CloneUrl: repoGet.GetCloneURL(),
HookType: githubHook.GetType(),
HookName: githubHook.GetName(),
HookUuid: hookUUID,
IsPrivate: repoGet.GetPrivate(),
IsFork: repoGet.GetFork(),
DefaultBranch: repoGet.GetDefaultBranch(),
License: repoGet.GetLicense().GetSPDXID(),
}

return regResult, nil
}

func (s *Server) parseGithubEventForProcessing(
rawWHPayload []byte,
msg *message.Message,
Expand Down
111 changes: 88 additions & 23 deletions internal/controlplane/handlers_repositories.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ import (
"github.com/stacklok/minder/internal/projects/features"
"github.com/stacklok/minder/internal/providers"
github "github.com/stacklok/minder/internal/providers/github"
"github.com/stacklok/minder/internal/reconcilers"
"github.com/stacklok/minder/internal/util"
cursorutil "github.com/stacklok/minder/internal/util/cursor"
"github.com/stacklok/minder/internal/util/ptr"
pb "github.com/stacklok/minder/pkg/api/protobuf/go/minder/v1"
v1 "github.com/stacklok/minder/pkg/providers/v1"
)
Expand All @@ -46,41 +46,106 @@ const maxFetchLimit = 100
// Once a user had enrolled in a project (they have a valid token), they can register
// repositories to be monitored by the minder by provisioning a webhook on the
// repository(ies).
func (s *Server) RegisterRepository(
ctx context.Context,
in *pb.RegisterRepositoryRequest,
) (*pb.RegisterRepositoryResponse, error) {
projectID, client, err := s.getProjectIDAndClient(ctx, in)
func (s *Server) RegisterRepository(ctx context.Context,
in *pb.RegisterRepositoryRequest) (*pb.RegisterRepositoryResponse, error) {
entityCtx := engine.EntityFromContext(ctx)
projectID := entityCtx.Project.ID

provider, err := getProviderFromRequestOrDefault(ctx, s.store, in, projectID)
if err != nil {
return nil, err
return nil, providerError(err)
}

pbOpts := []providers.ProviderBuilderOption{
providers.WithProviderMetrics(s.provMt),
providers.WithRestClientCache(s.restClientCache),
}
p, err := providers.GetProviderBuilder(ctx, provider, s.store, s.cryptoEngine, pbOpts...)
if err != nil {
return nil, status.Errorf(codes.Internal, "cannot get provider builder: %v", err)
}

// Unmarshal the in.GetRepositories() into a struct Repository
if in.GetRepository() == nil || in.GetRepository().Name == "" {
return nil, util.UserVisibleError(codes.InvalidArgument, "no repository provided")
}

// Validate that the Repository struct in the request
repoReference := in.GetRepository()
if repoReference == nil || repoReference.Name == "" || repoReference.Owner == "" {
return nil, util.UserVisibleError(codes.InvalidArgument, "missing repository owner and/or name")
repo := in.GetRepository()

result, err := s.registerWebhookForRepository(ctx, p, projectID, repo)
if err != nil {
return nil, util.UserVisibleError(codes.Internal, "cannot register webhook: %v", err)
}

r := result.Repository

response := &pb.RegisterRepositoryResponse{
Result: &pb.RegisterRepoResult{
Status: &pb.RegisterRepoResult_Status{
Success: false,
},
Result: result,
}

// Convert each result to a pb.Repository object
if result.Status.Error != nil {
return response, nil
}

// update the database
dbRepo, err := s.store.CreateRepository(ctx, db.CreateRepositoryParams{
Provider: provider.Name,
ProviderID: provider.ID,
ProjectID: projectID,
RepoOwner: r.Owner,
RepoName: r.Name,
RepoID: r.RepoId,
IsPrivate: r.IsPrivate,
IsFork: r.IsFork,
WebhookID: sql.NullInt64{
Int64: r.HookId,
Valid: true,
},
CloneUrl: r.CloneUrl,
WebhookUrl: r.HookUrl,
DeployUrl: r.DeployUrl,
DefaultBranch: sql.NullString{
String: r.DefaultBranch,
Valid: true,
},
License: sql.NullString{
String: r.License,
Valid: true,
},
})
// even if we set the webhook, if we couldn't create it in the database, we'll return an error
if err != nil {
log.Printf("error creating repository '%s/%s' in database: %v", r.Owner, r.Name, err)

result.Status.Success = false
errorStr := "error creating repository in database"
result.Status.Error = &errorStr
return response, nil
}

// To be backwards compatible with the existing implementation, we return
// an 200-type response with any errors inside the response body once we
// validate the response body
newRepo, err := s.repos.CreateRepository(ctx, client, projectID, repoReference)
repoDBID := dbRepo.ID.String()
r.Id = &repoDBID

// publish a reconciling event for the registered repositories
log.Printf("publishing register event for repository: %s/%s", r.Owner, r.Name)

msg, err := reconcilers.NewRepoReconcilerMessage(provider.Name, r.RepoId, projectID)
if err != nil {
log.Printf("error while registering repository: %v", err)
response.Result.Status.Error = ptr.Ptr(err.Error())
log.Printf("error creating reconciler event: %v", err)
return response, nil
}

response.Result.Status.Success = true
response.Result.Repository = newRepo
// This is a non-fatal error, so we'll just log it and continue with the next ones
if err := s.evt.Publish(reconcilers.InternalReconcilerEventTopic, msg); err != nil {
log.Printf("error publishing reconciler event: %v", err)
}

// Telemetry logging
logger.BusinessRecord(ctx).Provider = provider.Name
logger.BusinessRecord(ctx).Project = projectID
logger.BusinessRecord(ctx).Repository = dbRepo.ID

return response, nil
}

Expand Down
Loading

0 comments on commit a0d83dc

Please sign in to comment.