Compare commits
4 Commits
37dfa6edc4
...
a9c7fddfaf
| Author | SHA1 | Date | |
|---|---|---|---|
| a9c7fddfaf | |||
| 7f418927e7 | |||
| 90328001e5 | |||
| c4a1f5575c |
@@ -1510,7 +1510,14 @@ func (s *Store) ListProjectIDsForUser(userID string) ([]string, error) {
|
|||||||
|
|
||||||
func (s *Store) UpdateRepo(repo models.Repo) error {
|
func (s *Store) UpdateRepo(repo models.Repo) error {
|
||||||
var err error
|
var err error
|
||||||
_, err = s.Exec(`UPDATE repos SET name = ?, description = ?, path = ? WHERE public_id = ?`, repo.Name, repo.Description, repo.Path, repo.ID)
|
var result sql.Result
|
||||||
|
var rowsAffected int64
|
||||||
|
|
||||||
|
result, err = s.Exec(`UPDATE repos SET name = ?, description = ?, path = ? WHERE public_id = ?`, repo.Name, repo.Description, repo.Path, repo.ID)
|
||||||
|
if err != nil { return err }
|
||||||
|
rowsAffected, err = result.RowsAffected()
|
||||||
|
if err != nil { return err }
|
||||||
|
if rowsAffected <= 0 { return sql.ErrNoRows }
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -363,45 +363,44 @@ func (api *API) createACMEOrder(ctx context.Context, profileID string, commonNam
|
|||||||
var certKeyPEM string
|
var certKeyPEM string
|
||||||
var csrPEM string
|
var csrPEM string
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
api.Logger.Write(acmeLogID, codit_logger.LOG_INFO, "order create requested profile=%s cn=%s san_count=%d", strings.TrimSpace(profileID), strings.TrimSpace(commonName), len(sanDNS))
|
api.Logger.Write(acmeLogID, codit_logger.LOG_INFO, "order create requested profile=%s cn=%s san_count=%d", strings.TrimSpace(profileID), strings.TrimSpace(commonName), len(sanDNS))
|
||||||
if strings.TrimSpace(profileID) == "" {
|
if strings.TrimSpace(profileID) == "" { return order, errors.New("profile_id is required") }
|
||||||
return order, errors.New("profile_id is required")
|
if strings.TrimSpace(commonName) == "" { return order, errors.New("common_name is required") }
|
||||||
}
|
|
||||||
if strings.TrimSpace(commonName) == "" {
|
|
||||||
return order, errors.New("common_name is required")
|
|
||||||
}
|
|
||||||
profile, err = api.storeContext(ctx).GetACMEProfile(strings.TrimSpace(profileID))
|
profile, err = api.storeContext(ctx).GetACMEProfile(strings.TrimSpace(profileID))
|
||||||
if err != nil {
|
if err != nil { return order, err }
|
||||||
return order, err
|
if !profile.Enabled { return order, errors.New("ACME profile is disabled") }
|
||||||
}
|
|
||||||
if !profile.Enabled {
|
|
||||||
return order, errors.New("ACME profile is disabled")
|
|
||||||
}
|
|
||||||
domains = normalizeACMEDomains(commonName, sanDNS)
|
domains = normalizeACMEDomains(commonName, sanDNS)
|
||||||
if len(domains) == 0 {
|
if len(domains) == 0 { return order, errors.New("at least one domain is required") }
|
||||||
return order, errors.New("at least one domain is required")
|
|
||||||
}
|
|
||||||
for i = 0; i < len(domains); i++ {
|
for i = 0; i < len(domains); i++ {
|
||||||
domainIDs = append(domainIDs, acme.AuthzID{Type: "dns", Value: domains[i]})
|
domainIDs = append(domainIDs, acme.AuthzID{Type: "dns", Value: domains[i]})
|
||||||
}
|
}
|
||||||
|
|
||||||
certKeyPEM, csrPEM, _, err = generateACMECSR(domains[0], domains)
|
certKeyPEM, csrPEM, _, err = generateACMECSR(domains[0], domains)
|
||||||
if err != nil {
|
if err != nil { return order, err }
|
||||||
return order, err
|
|
||||||
}
|
// attemp to ensure the acme profiel h as a usable acme account registered with the ca
|
||||||
|
// it makes http/https calls to the acme server(profile.DirectoryURL). so it must not
|
||||||
|
// be held in a transaction.
|
||||||
client, accountKey, profile, err = api.ensureACMEAccount(ctx, profile)
|
client, accountKey, profile, err = api.ensureACMEAccount(ctx, profile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.Logger.Write(acmeLogID, codit_logger.LOG_WARN, "order create failed profile=%s step=account err=%v", profile.ID, err)
|
api.Logger.Write(acmeLogID, codit_logger.LOG_WARN, "order create failed profile=%s step=account err=%v", profile.ID, err)
|
||||||
_ = api.storeContext(ctx).UpdateACMEProfileLastError(profile.ID, err.Error())
|
_ = api.persistACMEProfileLastError(profile.ID, err.Error())
|
||||||
return order, err
|
return order, err
|
||||||
}
|
}
|
||||||
_ = accountKey
|
_ = accountKey
|
||||||
|
|
||||||
api.Logger.Write(acmeLogID, codit_logger.LOG_INFO, "order create account ready profile=%s account=%s", profile.ID, profile.AccountURL)
|
api.Logger.Write(acmeLogID, codit_logger.LOG_INFO, "order create account ready profile=%s account=%s", profile.ID, profile.AccountURL)
|
||||||
rawOrder, err = client.AuthorizeOrder(ctx, domainIDs)
|
rawOrder, err = client.AuthorizeOrder(ctx, domainIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.Logger.Write(acmeLogID, codit_logger.LOG_WARN, "order create failed profile=%s step=authorize_order err=%v", profile.ID, err)
|
api.Logger.Write(acmeLogID, codit_logger.LOG_WARN, "order create failed profile=%s step=authorize_order err=%v", profile.ID, err)
|
||||||
_ = api.storeContext(ctx).UpdateACMEProfileLastError(profile.ID, err.Error())
|
_ = api.persistACMEProfileLastError(profile.ID, err.Error())
|
||||||
return order, err
|
return order, err
|
||||||
}
|
}
|
||||||
|
|
||||||
api.Logger.Write(acmeLogID, codit_logger.LOG_INFO, "order created upstream profile=%s order_url=%s authz_count=%d", profile.ID, rawOrder.URI, len(rawOrder.AuthzURLs))
|
api.Logger.Write(acmeLogID, codit_logger.LOG_INFO, "order created upstream profile=%s order_url=%s authz_count=%d", profile.ID, rawOrder.URI, len(rawOrder.AuthzURLs))
|
||||||
for i = 0; i < len(rawOrder.AuthzURLs); i++ {
|
for i = 0; i < len(rawOrder.AuthzURLs); i++ {
|
||||||
authz, err = client.GetAuthorization(ctx, rawOrder.AuthzURLs[i])
|
authz, err = client.GetAuthorization(ctx, rawOrder.AuthzURLs[i])
|
||||||
@@ -415,13 +414,11 @@ func (api *API) createACMEOrder(ctx context.Context, profileID string, commonNam
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ch == nil {
|
if ch == nil { return order, errors.New("dns-01 challenge not offered by ACME CA") }
|
||||||
return order, errors.New("dns-01 challenge not offered by ACME CA")
|
|
||||||
}
|
|
||||||
txt, err = client.DNS01ChallengeRecord(ch.Token)
|
txt, err = client.DNS01ChallengeRecord(ch.Token)
|
||||||
if err != nil {
|
if err != nil { return order, err }
|
||||||
return order, err
|
|
||||||
}
|
|
||||||
challenges = append(challenges, models.ACMEDNSChallenge{
|
challenges = append(challenges, models.ACMEDNSChallenge{
|
||||||
AuthorizationURL: rawOrder.AuthzURLs[i],
|
AuthorizationURL: rawOrder.AuthzURLs[i],
|
||||||
Identifier: authz.Identifier.Value,
|
Identifier: authz.Identifier.Value,
|
||||||
@@ -446,13 +443,12 @@ func (api *API) createACMEOrder(ctx context.Context, profileID string, commonNam
|
|||||||
CSRPEM: csrPEM,
|
CSRPEM: csrPEM,
|
||||||
KeyPEM: certKeyPEM,
|
KeyPEM: certKeyPEM,
|
||||||
}
|
}
|
||||||
order, err = api.storeContext(ctx).CreateACMEOrder(order)
|
order, err = api.persistACMEOrderCreate(order, profile.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.Logger.Write(acmeLogID, codit_logger.LOG_WARN, "order create persist failed profile=%s order_url=%s err=%v", profile.ID, rawOrder.URI, err)
|
api.Logger.Write(acmeLogID, codit_logger.LOG_WARN, "order create persist failed profile=%s order_url=%s err=%v", profile.ID, rawOrder.URI, err)
|
||||||
return order, err
|
return order, err
|
||||||
}
|
}
|
||||||
api.Logger.Write(acmeLogID, codit_logger.LOG_INFO, "order create success id=%s profile=%s cn=%s challenge_count=%d", order.ID, order.ProfileID, order.CommonName, len(order.Challenges))
|
api.Logger.Write(acmeLogID, codit_logger.LOG_INFO, "order create success id=%s profile=%s cn=%s challenge_count=%d", order.ID, order.ProfileID, order.CommonName, len(order.Challenges))
|
||||||
_ = api.storeContext(ctx).UpdateACMEProfileLastError(profile.ID, "")
|
|
||||||
return order, nil
|
return order, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -670,7 +666,7 @@ func (api *API) persistACMEFinalizeFailure(order models.ACMEOrder, profileID str
|
|||||||
var store *db.Store
|
var store *db.Store
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
ctx, cancel = context.WithTimeout(context.Background(), 10 * time.Second)
|
ctx, cancel = context.WithTimeout(context.Background(), db.IndependentDBOperationTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
store, err = api.Store.BeginImmediateStore(ctx)
|
store, err = api.Store.BeginImmediateStore(ctx)
|
||||||
@@ -707,7 +703,7 @@ func (api *API) persistACMEFinalizeSuccess(order models.ACMEOrder, profileID str
|
|||||||
var existing models.PKICert
|
var existing models.PKICert
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
ctx, cancel = context.WithTimeout(context.Background(), 10 * time.Second)
|
ctx, cancel = context.WithTimeout(context.Background(), db.IndependentDBOperationTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
store, err = api.Store.BeginImmediateStore(ctx)
|
store, err = api.Store.BeginImmediateStore(ctx)
|
||||||
@@ -752,6 +748,83 @@ func (api *API) persistACMEFinalizeSuccess(order models.ACMEOrder, profileID str
|
|||||||
return order, cert, nil
|
return order, cert, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (api *API) persistACMEProfileAccount(profile models.ACMEProfile) error {
|
||||||
|
var ctx context.Context
|
||||||
|
var cancel context.CancelFunc
|
||||||
|
var store *db.Store
|
||||||
|
var err error
|
||||||
|
|
||||||
|
ctx, cancel = context.WithTimeout(context.Background(), db.IndependentDBOperationTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
store, err = api.Store.BeginImmediateStore(ctx)
|
||||||
|
if err != nil { return err }
|
||||||
|
err = store.UpdateACMEProfileAccount(profile.ID, profile.AccountURL, profile.AccountKeyPEM)
|
||||||
|
if err != nil {
|
||||||
|
_ = store.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = store.Commit()
|
||||||
|
if err != nil {
|
||||||
|
_ = store.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *API) persistACMEProfileLastError(profileID string, msg string) error {
|
||||||
|
var ctx context.Context
|
||||||
|
var cancel context.CancelFunc
|
||||||
|
var store *db.Store
|
||||||
|
var err error
|
||||||
|
|
||||||
|
ctx, cancel = context.WithTimeout(context.Background(), db.IndependentDBOperationTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
store, err = api.Store.BeginImmediateStore(ctx)
|
||||||
|
if err != nil { return err }
|
||||||
|
err = store.UpdateACMEProfileLastError(profileID, msg)
|
||||||
|
if err != nil {
|
||||||
|
_ = store.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = store.Commit()
|
||||||
|
if err != nil {
|
||||||
|
_ = store.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *API) persistACMEOrderCreate(order models.ACMEOrder, profileID string) (models.ACMEOrder, error) {
|
||||||
|
var ctx context.Context
|
||||||
|
var cancel context.CancelFunc
|
||||||
|
var store *db.Store
|
||||||
|
var err error
|
||||||
|
|
||||||
|
ctx, cancel = context.WithTimeout(context.Background(), db.IndependentDBOperationTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
store, err = api.Store.BeginImmediateStore(ctx)
|
||||||
|
if err != nil { return order, err }
|
||||||
|
order, err = store.CreateACMEOrder(order)
|
||||||
|
if err != nil {
|
||||||
|
_ = store.Rollback()
|
||||||
|
return order, err
|
||||||
|
}
|
||||||
|
err = store.UpdateACMEProfileLastError(profileID, "")
|
||||||
|
if err != nil {
|
||||||
|
_ = store.Rollback()
|
||||||
|
return order, err
|
||||||
|
}
|
||||||
|
err = store.Commit()
|
||||||
|
if err != nil {
|
||||||
|
_ = store.Rollback()
|
||||||
|
return order, err
|
||||||
|
}
|
||||||
|
return order, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (api *API) DeleteACMEOrder(w http.ResponseWriter, r *http.Request, params map[string]string) {
|
func (api *API) DeleteACMEOrder(w http.ResponseWriter, r *http.Request, params map[string]string) {
|
||||||
var err error
|
var err error
|
||||||
if !api.requireAdmin(w, r) {
|
if !api.requireAdmin(w, r) {
|
||||||
@@ -774,17 +847,15 @@ func (api *API) ensureACMEAccount(ctx context.Context, profile models.ACMEProfil
|
|||||||
var contact []string
|
var contact []string
|
||||||
var keyPEM string
|
var keyPEM string
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
if strings.TrimSpace(profile.AccountKeyPEM) == "" {
|
if strings.TrimSpace(profile.AccountKeyPEM) == "" {
|
||||||
key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
if err != nil {
|
if err != nil { return nil, nil, profile, err }
|
||||||
return nil, nil, profile, err
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
key, err = parseECPrivateKeyPEM(profile.AccountKeyPEM)
|
key, err = parseECPrivateKeyPEM(profile.AccountKeyPEM)
|
||||||
if err != nil {
|
if err != nil { return nil, nil, profile, err }
|
||||||
return nil, nil, profile, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
client = &acme.Client{Key: key, DirectoryURL: strings.TrimSpace(profile.DirectoryURL), UserAgent: api.serverId()+"-acme"}
|
client = &acme.Client{Key: key, DirectoryURL: strings.TrimSpace(profile.DirectoryURL), UserAgent: api.serverId()+"-acme"}
|
||||||
if strings.TrimSpace(profile.AccountURL) != "" {
|
if strings.TrimSpace(profile.AccountURL) != "" {
|
||||||
client.KID = acme.KeyID(strings.TrimSpace(profile.AccountURL))
|
client.KID = acme.KeyID(strings.TrimSpace(profile.AccountURL))
|
||||||
@@ -805,18 +876,15 @@ func (api *API) ensureACMEAccount(ctx context.Context, profile models.ACMEProfil
|
|||||||
return nil, nil, profile, err
|
return nil, nil, profile, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if account != nil {
|
if account != nil { profile.AccountURL = account.URI }
|
||||||
profile.AccountURL = account.URI
|
|
||||||
}
|
|
||||||
keyPEM, err = encodeECPrivateKeyPEM(key)
|
keyPEM, err = encodeECPrivateKeyPEM(key)
|
||||||
if err != nil {
|
if err != nil { return nil, nil, profile, err }
|
||||||
return nil, nil, profile, err
|
|
||||||
}
|
|
||||||
profile.AccountKeyPEM = keyPEM
|
profile.AccountKeyPEM = keyPEM
|
||||||
err = api.storeContext(ctx).UpdateACMEProfileAccount(profile.ID, profile.AccountURL, profile.AccountKeyPEM)
|
err = api.persistACMEProfileAccount(profile)
|
||||||
if err != nil {
|
if err != nil { return nil, nil, profile, err }
|
||||||
return nil, nil, profile, err
|
|
||||||
}
|
|
||||||
client.KID = acme.KeyID(strings.TrimSpace(profile.AccountURL))
|
client.KID = acme.KeyID(strings.TrimSpace(profile.AccountURL))
|
||||||
return client, key, profile, nil
|
return client, key, profile, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2207,24 +2207,37 @@ func (api *API) DeleteProject(w http.ResponseWriter, r *http.Request, params map
|
|||||||
var err error
|
var err error
|
||||||
var repos []models.Repo
|
var repos []models.Repo
|
||||||
var currentRepos []models.Repo
|
var currentRepos []models.Repo
|
||||||
var running bool
|
|
||||||
var i int
|
var i int
|
||||||
var tempPaths []string
|
var tempPaths []string
|
||||||
var sourcePaths []string
|
var sourcePaths []string
|
||||||
var temp string
|
var temp string
|
||||||
var txStore *db.Store
|
var txStore *db.Store
|
||||||
if !api.requireProjectRole(w, r, params["id"], models.RoleAdmin) {
|
|
||||||
return
|
if !api.requireProjectRole(w, r, params["id"], models.RoleAdmin) { return }
|
||||||
}
|
|
||||||
repos, err = api.store(r).ListReposOwned(params["id"])
|
repos, err = api.store(r).ListReposOwned(params["id"])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
|
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prevent deletion if a repo row is still in the provisioning window.
|
||||||
|
// This can happen due to race condition with CreateRepo.
|
||||||
|
// CreateRepo has two transactions:
|
||||||
|
// 1. Short DB transaction creates repo row with path="".
|
||||||
|
// 2. Transaction commits.
|
||||||
|
// 3. Handler initializes filesystem/git storage.
|
||||||
|
// 4. Second short DB transaction updates the repo row with the final path.
|
||||||
|
// If DeleteProject comes while 3 is ongoing, there is a risk.
|
||||||
for i = 0; i < len(repos); i++ {
|
for i = 0; i < len(repos); i++ {
|
||||||
if repos[i].Type != models.RepoTypeRPM {
|
if repos[i].Path == "" {
|
||||||
continue
|
WriteJSON(w, http.StatusConflict, map[string]string{"error": "repository storage is not ready", "repo_id": repos[i].ID, "repo_name": repos[i].Name})
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if repos[i].Type == models.RepoTypeRPM {
|
||||||
|
var running bool
|
||||||
|
|
||||||
running, err = api.store(r).HasRunningRPMMirrorTask(repos[i].ID)
|
running, err = api.store(r).HasRunningRPMMirrorTask(repos[i].ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
|
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
|
||||||
@@ -2235,12 +2248,12 @@ func (api *API) DeleteProject(w http.ResponseWriter, r *http.Request, params map
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tempPaths = make([]string, 0, len(repos))
|
tempPaths = make([]string, 0, len(repos))
|
||||||
sourcePaths = make([]string, 0, len(repos))
|
sourcePaths = make([]string, 0, len(repos))
|
||||||
for i = 0; i < len(repos); i++ {
|
for i = 0; i < len(repos); i++ {
|
||||||
if repos[i].Path == "" {
|
if repos[i].Path == "" { continue }
|
||||||
continue
|
|
||||||
}
|
|
||||||
temp, err = moveToTrash(repos[i].Path)
|
temp, err = moveToTrash(repos[i].Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
restoreFromTrash(tempPaths, sourcePaths)
|
restoreFromTrash(tempPaths, sourcePaths)
|
||||||
@@ -2250,6 +2263,7 @@ func (api *API) DeleteProject(w http.ResponseWriter, r *http.Request, params map
|
|||||||
tempPaths = append(tempPaths, temp)
|
tempPaths = append(tempPaths, temp)
|
||||||
sourcePaths = append(sourcePaths, repos[i].Path)
|
sourcePaths = append(sourcePaths, repos[i].Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
txStore, err = api.Store.BeginImmediateStore(r.Context())
|
txStore, err = api.Store.BeginImmediateStore(r.Context())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
restoreFromTrash(tempPaths, sourcePaths)
|
restoreFromTrash(tempPaths, sourcePaths)
|
||||||
@@ -2269,10 +2283,16 @@ func (api *API) DeleteProject(w http.ResponseWriter, r *http.Request, params map
|
|||||||
WriteJSONWithErrorReason(w, r, http.StatusConflict, "project repositories changed while deletion was in progress")
|
WriteJSONWithErrorReason(w, r, http.StatusConflict, "project repositories changed while deletion was in progress")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for i = 0; i < len(currentRepos); i++ {
|
for i = 0; i < len(currentRepos); i++ {
|
||||||
if currentRepos[i].Type != models.RepoTypeRPM {
|
if currentRepos[i].Path == "" {
|
||||||
continue
|
_ = txStore.Rollback()
|
||||||
|
restoreFromTrash(tempPaths, sourcePaths)
|
||||||
|
WriteJSON(w, http.StatusConflict, map[string]string{"error": "repository storage is not ready", "repo_id": currentRepos[i].ID, "repo_name": currentRepos[i].Name})
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
if currentRepos[i].Type == models.RepoTypeRPM {
|
||||||
|
var running bool
|
||||||
running, err = txStore.HasRunningRPMMirrorTask(currentRepos[i].ID)
|
running, err = txStore.HasRunningRPMMirrorTask(currentRepos[i].ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = txStore.Rollback()
|
_ = txStore.Rollback()
|
||||||
@@ -2287,6 +2307,7 @@ func (api *API) DeleteProject(w http.ResponseWriter, r *http.Request, params map
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
err = txStore.DeleteProject(params["id"])
|
err = txStore.DeleteProject(params["id"])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = txStore.Rollback()
|
_ = txStore.Rollback()
|
||||||
@@ -2673,7 +2694,6 @@ func (api *API) GetRepo(w http.ResponseWriter, r *http.Request, params map[strin
|
|||||||
|
|
||||||
func (api *API) CreateRepo(w http.ResponseWriter, r *http.Request, params map[string]string) {
|
func (api *API) CreateRepo(w http.ResponseWriter, r *http.Request, params map[string]string) {
|
||||||
var req createRepoRequest
|
var req createRepoRequest
|
||||||
var err error
|
|
||||||
var user models.User
|
var user models.User
|
||||||
var repoID string
|
var repoID string
|
||||||
var project models.Project
|
var project models.Project
|
||||||
@@ -2683,13 +2703,16 @@ func (api *API) CreateRepo(w http.ResponseWriter, r *http.Request, params map[st
|
|||||||
var repoType string
|
var repoType string
|
||||||
var projectStorageID int64
|
var projectStorageID int64
|
||||||
var repoStorageID int64
|
var repoStorageID int64
|
||||||
var ok bool
|
|
||||||
var exists bool
|
var exists bool
|
||||||
var txStore *db.Store
|
var txStore *db.Store
|
||||||
|
var cloneURL string
|
||||||
|
var rpmURL string
|
||||||
|
var dockerURL string
|
||||||
|
var ok bool
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if !api.requireProjectRole(w, r, params["projectId"], models.RoleWriter) { return }
|
||||||
|
|
||||||
if !api.requireProjectRole(w, r, params["projectId"], models.RoleWriter) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = DecodeJSON(r, &req)
|
err = DecodeJSON(r, &req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
WriteJSONWithErrorReason(w, r, http.StatusBadRequest, "invalid json")
|
WriteJSONWithErrorReason(w, r, http.StatusBadRequest, "invalid json")
|
||||||
@@ -2708,12 +2731,14 @@ func (api *API) CreateRepo(w http.ResponseWriter, r *http.Request, params map[st
|
|||||||
WriteJSONWithErrorReason(w, r, http.StatusBadRequest, "unsupported repo type")
|
WriteJSONWithErrorReason(w, r, http.StatusBadRequest, "unsupported repo type")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user, _ = middleware.UserFromContext(r.Context())
|
user, _ = middleware.UserFromContext(r.Context())
|
||||||
repoID, err = util.NewID()
|
repoID, err = util.NewID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, "failed to create repo")
|
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, "failed to create repo")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
repo = models.Repo{
|
repo = models.Repo{
|
||||||
ID: repoID,
|
ID: repoID,
|
||||||
ProjectID: params["projectId"],
|
ProjectID: params["projectId"],
|
||||||
@@ -2723,6 +2748,7 @@ func (api *API) CreateRepo(w http.ResponseWriter, r *http.Request, params map[st
|
|||||||
Path: "",
|
Path: "",
|
||||||
CreatedBy: user.ID,
|
CreatedBy: user.ID,
|
||||||
}
|
}
|
||||||
|
|
||||||
txStore, err = api.Store.BeginImmediateStore(r.Context())
|
txStore, err = api.Store.BeginImmediateStore(r.Context())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
|
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
|
||||||
@@ -2739,30 +2765,38 @@ func (api *API) CreateRepo(w http.ResponseWriter, r *http.Request, params map[st
|
|||||||
WriteJSONWithErrorReason(w, r, http.StatusConflict, "repository name already exists")
|
WriteJSONWithErrorReason(w, r, http.StatusConflict, "repository name already exists")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
project, err = txStore.GetProject(params["projectId"])
|
project, err = txStore.GetProject(params["projectId"])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = txStore.Rollback()
|
_ = txStore.Rollback()
|
||||||
WriteJSONWithErrorReason(w, r, http.StatusNotFound, "project not found")
|
WriteJSONWithErrorReason(w, r, http.StatusNotFound, "project not found")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
created, err = txStore.CreateRepo(repo)
|
created, err = txStore.CreateRepo(repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = txStore.Rollback()
|
_ = txStore.Rollback()
|
||||||
WriteJSONWithErrorReason(w, r, http.StatusBadRequest, err.Error())
|
WriteJSONWithErrorReason(w, r, http.StatusBadRequest, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
projectStorageID, repoStorageID, err = txStore.GetRepoStorageIDs(created.ID)
|
projectStorageID, repoStorageID, err = txStore.GetRepoStorageIDs(created.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = txStore.Rollback()
|
_ = txStore.Rollback()
|
||||||
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
|
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = txStore.Commit()
|
err = txStore.Commit()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = txStore.Rollback()
|
_ = txStore.Rollback()
|
||||||
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
|
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we need to settle the file system parts. this is outside the transaction
|
||||||
|
// upon failure, deleting the row created above is important.
|
||||||
|
// api.deleteRepoRecord() has its own write transaction.
|
||||||
repoPath = api.repoStoragePathByType(created.Type, projectStorageID, repoStorageID)
|
repoPath = api.repoStoragePathByType(created.Type, projectStorageID, repoStorageID)
|
||||||
if created.Type == models.RepoTypeGit {
|
if created.Type == models.RepoTypeGit {
|
||||||
err = os.MkdirAll(filepath.Dir(repoPath), 0o755)
|
err = os.MkdirAll(filepath.Dir(repoPath), 0o755)
|
||||||
@@ -2773,23 +2807,25 @@ func (api *API) CreateRepo(w http.ResponseWriter, r *http.Request, params map[st
|
|||||||
err = os.MkdirAll(repoPath, 0o755)
|
err = os.MkdirAll(repoPath, 0o755)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = api.deleteRepoRecord(r.Context(), created.ID)
|
_ = api.deleteRepoRecord(created.ID)
|
||||||
_ = os.RemoveAll(repoPath)
|
_ = os.RemoveAll(repoPath)
|
||||||
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
|
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
created.Path = repoPath
|
created.Path = repoPath
|
||||||
txStore, err = api.Store.BeginImmediateStore(r.Context())
|
txStore, err = api.Store.BeginImmediateStore(r.Context())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = api.deleteRepoRecord(r.Context(), created.ID)
|
_ = api.deleteRepoRecord(created.ID)
|
||||||
_ = os.RemoveAll(repoPath)
|
_ = os.RemoveAll(repoPath)
|
||||||
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
|
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = txStore.UpdateRepo(created)
|
err = txStore.UpdateRepo(created)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = txStore.Rollback()
|
_ = txStore.Rollback()
|
||||||
_ = api.deleteRepoRecord(r.Context(), created.ID)
|
_ = api.deleteRepoRecord(created.ID)
|
||||||
_ = os.RemoveAll(repoPath)
|
_ = os.RemoveAll(repoPath)
|
||||||
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
|
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
@@ -2797,23 +2833,16 @@ func (api *API) CreateRepo(w http.ResponseWriter, r *http.Request, params map[st
|
|||||||
err = txStore.Commit()
|
err = txStore.Commit()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = txStore.Rollback()
|
_ = txStore.Rollback()
|
||||||
_ = api.deleteRepoRecord(r.Context(), created.ID)
|
_ = api.deleteRepoRecord(created.ID)
|
||||||
_ = os.RemoveAll(repoPath)
|
_ = os.RemoveAll(repoPath)
|
||||||
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
|
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var cloneURL string
|
|
||||||
var rpmURL string
|
if created.Type == models.RepoTypeGit { cloneURL = api.cloneURL(project.Slug, created.Name) }
|
||||||
var dockerURL string
|
if created.Type == models.RepoTypeRPM { rpmURL = api.rpmURL(project.Slug, created.Name) }
|
||||||
if created.Type == models.RepoTypeGit {
|
if created.Type == models.RepoTypeDocker { dockerURL = api.dockerURL(project.Slug, created.Name) }
|
||||||
cloneURL = api.cloneURL(project.Slug, created.Name)
|
|
||||||
}
|
|
||||||
if created.Type == models.RepoTypeRPM {
|
|
||||||
rpmURL = api.rpmURL(project.Slug, created.Name)
|
|
||||||
}
|
|
||||||
if created.Type == models.RepoTypeDocker {
|
|
||||||
dockerURL = api.dockerURL(project.Slug, created.Name)
|
|
||||||
}
|
|
||||||
WriteJSON(w, http.StatusCreated, repoResponse{Repo: created, CloneURL: cloneURL, RPMURL: rpmURL, DockerURL: dockerURL})
|
WriteJSON(w, http.StatusCreated, repoResponse{Repo: created, CloneURL: cloneURL, RPMURL: rpmURL, DockerURL: dockerURL})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3004,6 +3033,10 @@ func (api *API) UpdateRepo(w http.ResponseWriter, r *http.Request, params map[st
|
|||||||
repo.Path = newPath
|
repo.Path = newPath
|
||||||
err = api.store(r).UpdateRepo(repo)
|
err = api.store(r).UpdateRepo(repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
WriteJSONWithErrorReason(w, r, http.StatusNotFound, "repo not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
|
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -3024,109 +3057,137 @@ func (api *API) UpdateRepo(w http.ResponseWriter, r *http.Request, params map[st
|
|||||||
|
|
||||||
func (api *API) DeleteRepo(w http.ResponseWriter, r *http.Request, params map[string]string) {
|
func (api *API) DeleteRepo(w http.ResponseWriter, r *http.Request, params map[string]string) {
|
||||||
var repo models.Repo
|
var repo models.Repo
|
||||||
|
var current models.Repo
|
||||||
var project models.Project
|
var project models.Project
|
||||||
var running bool
|
var running bool
|
||||||
var err error
|
|
||||||
var temp string
|
var temp string
|
||||||
var txStore *db.Store
|
var txStore *db.Store
|
||||||
|
var cloneURL string
|
||||||
|
var rpmURL string
|
||||||
|
var dockerURL string
|
||||||
|
var err error
|
||||||
|
|
||||||
repo, err = api.getRepoResolved(api.store(r), params["id"])
|
repo, err = api.getRepoResolved(api.store(r), params["id"])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
WriteJSONWithErrorReason(w, r, http.StatusNotFound, "repo not found")
|
WriteJSONWithErrorReason(w, r, http.StatusNotFound, "repo not found")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !api.requireProjectRole(w, r, repo.ProjectID, models.RoleWriter) {
|
|
||||||
return
|
if !api.requireProjectRole(w, r, repo.ProjectID, models.RoleWriter) { return }
|
||||||
}
|
|
||||||
if repo.Type == models.RepoTypeRPM {
|
|
||||||
running, err = api.store(r).HasRunningRPMMirrorTask(repo.ID)
|
|
||||||
if err != nil {
|
|
||||||
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if running {
|
|
||||||
WriteJSONWithErrorReason(w, r, http.StatusConflict, "cannot delete repository while rpm mirror sync is running")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
project, err = api.store(r).GetProject(repo.ProjectID)
|
|
||||||
if err != nil {
|
|
||||||
WriteJSONWithErrorReason(w, r, http.StatusNotFound, "project not found")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if repo.Path != "" {
|
|
||||||
temp, err = moveToTrash(repo.Path)
|
|
||||||
if err != nil {
|
|
||||||
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
txStore, err = api.Store.BeginImmediateStore(r.Context())
|
txStore, err = api.Store.BeginImmediateStore(r.Context())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if temp != "" && repo.Path != "" {
|
|
||||||
_ = os.Rename(temp, repo.Path)
|
|
||||||
}
|
|
||||||
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
|
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// re-read to avoid race condition with CreateRepo which has uses two transactions
|
||||||
|
repo, err = txStore.GetRepo(params["id"])
|
||||||
|
if err != nil {
|
||||||
|
_ = txStore.Rollback()
|
||||||
|
WriteJSONWithErrorReason(w, r, http.StatusNotFound, "repo not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if repo.Path == "" {
|
||||||
|
_ = txStore.Rollback()
|
||||||
|
WriteJSONWithErrorReason(w, r, http.StatusConflict, "repository storage is not ready")
|
||||||
|
return
|
||||||
|
}
|
||||||
if repo.Type == models.RepoTypeRPM {
|
if repo.Type == models.RepoTypeRPM {
|
||||||
running, err = txStore.HasRunningRPMMirrorTask(repo.ID)
|
running, err = txStore.HasRunningRPMMirrorTask(repo.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = txStore.Rollback()
|
_ = txStore.Rollback()
|
||||||
if temp != "" && repo.Path != "" {
|
|
||||||
_ = os.Rename(temp, repo.Path)
|
|
||||||
}
|
|
||||||
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
|
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if running {
|
if running {
|
||||||
_ = txStore.Rollback()
|
_ = txStore.Rollback()
|
||||||
if temp != "" && repo.Path != "" {
|
|
||||||
_ = os.Rename(temp, repo.Path)
|
|
||||||
}
|
|
||||||
WriteJSONWithErrorReason(w, r, http.StatusConflict, "cannot delete repository while rpm mirror sync is running")
|
WriteJSONWithErrorReason(w, r, http.StatusConflict, "cannot delete repository while rpm mirror sync is running")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
project, err = txStore.GetProject(repo.ProjectID)
|
||||||
|
if err != nil {
|
||||||
|
_ = txStore.Rollback()
|
||||||
|
WriteJSONWithErrorReason(w, r, http.StatusNotFound, "project not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_ = txStore.Rollback()
|
||||||
|
|
||||||
|
temp, err = moveToTrash(repo.Path)
|
||||||
|
if err != nil {
|
||||||
|
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
txStore, err = api.Store.BeginImmediateStore(r.Context())
|
||||||
|
if err != nil {
|
||||||
|
_ = os.Rename(temp, repo.Path)
|
||||||
|
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
current, err = txStore.GetRepo(params["id"])
|
||||||
|
if err != nil {
|
||||||
|
_ = txStore.Rollback()
|
||||||
|
_ = os.Rename(temp, repo.Path)
|
||||||
|
WriteJSONWithErrorReason(w, r, http.StatusNotFound, "repo not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if current.ProjectID != repo.ProjectID || current.Path != repo.Path {
|
||||||
|
_ = txStore.Rollback()
|
||||||
|
_ = os.Rename(temp, repo.Path)
|
||||||
|
WriteJSONWithErrorReason(w, r, http.StatusConflict, "repository changed while deletion was in progress")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if repo.Type == models.RepoTypeRPM {
|
||||||
|
running, err = txStore.HasRunningRPMMirrorTask(repo.ID)
|
||||||
|
if err != nil {
|
||||||
|
_ = txStore.Rollback()
|
||||||
|
_ = os.Rename(temp, repo.Path)
|
||||||
|
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if running {
|
||||||
|
_ = txStore.Rollback()
|
||||||
|
_ = os.Rename(temp, repo.Path)
|
||||||
|
WriteJSONWithErrorReason(w, r, http.StatusConflict, "cannot delete repository while rpm mirror sync is running")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err = txStore.DeleteRepo(repo.ID)
|
err = txStore.DeleteRepo(repo.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = txStore.Rollback()
|
_ = txStore.Rollback()
|
||||||
if temp != "" && repo.Path != "" {
|
|
||||||
_ = os.Rename(temp, repo.Path)
|
_ = os.Rename(temp, repo.Path)
|
||||||
}
|
|
||||||
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
|
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = txStore.Commit()
|
err = txStore.Commit()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = txStore.Rollback()
|
_ = txStore.Rollback()
|
||||||
if temp != "" && repo.Path != "" {
|
|
||||||
_ = os.Rename(temp, repo.Path)
|
_ = os.Rename(temp, repo.Path)
|
||||||
}
|
|
||||||
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
|
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if temp != "" {
|
|
||||||
_ = os.RemoveAll(temp)
|
_ = os.RemoveAll(temp)
|
||||||
}
|
|
||||||
var cloneURL string
|
if repo.Type == models.RepoTypeGit { cloneURL = api.cloneURL(project.Slug, repo.Name) }
|
||||||
var rpmURL string
|
if repo.Type == models.RepoTypeRPM { rpmURL = api.rpmURL(project.Slug, repo.Name) }
|
||||||
var dockerURL string
|
if repo.Type == models.RepoTypeDocker { dockerURL = api.dockerURL(project.Slug, repo.Name) }
|
||||||
if repo.Type == models.RepoTypeGit {
|
|
||||||
cloneURL = api.cloneURL(project.Slug, repo.Name)
|
|
||||||
}
|
|
||||||
if repo.Type == models.RepoTypeRPM {
|
|
||||||
rpmURL = api.rpmURL(project.Slug, repo.Name)
|
|
||||||
}
|
|
||||||
if repo.Type == models.RepoTypeDocker {
|
|
||||||
dockerURL = api.dockerURL(project.Slug, repo.Name)
|
|
||||||
}
|
|
||||||
WriteJSON(w, http.StatusOK, repoResponse{Repo: repo, CloneURL: cloneURL, RPMURL: rpmURL, DockerURL: dockerURL})
|
WriteJSON(w, http.StatusOK, repoResponse{Repo: repo, CloneURL: cloneURL, RPMURL: rpmURL, DockerURL: dockerURL})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) deleteRepoRecord(ctx context.Context, repoID string) error {
|
func (api *API) deleteRepoRecord(repoID string) error {
|
||||||
|
var ctx context.Context
|
||||||
|
var cancel context.CancelFunc
|
||||||
var txStore *db.Store
|
var txStore *db.Store
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
ctx, cancel = context.WithTimeout(context.Background(), db.IndependentDBOperationTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
txStore, err = api.Store.BeginImmediateStore(ctx)
|
txStore, err = api.Store.BeginImmediateStore(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ func (api *API) finishSSHFileTransferAudit(audit *sshFileTransferAudit, status s
|
|||||||
audit.Finished = true
|
audit.Finished = true
|
||||||
|
|
||||||
// Use an independent bounded context so client cancellation does not skip the final audit update.
|
// Use an independent bounded context so client cancellation does not skip the final audit update.
|
||||||
ctx, cancel = context.WithTimeout(context.Background(), 10 * time.Second)
|
ctx, cancel = context.WithTimeout(context.Background(), db.IndependentDBOperationTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
err = audit.Store.FinishSSHFileTransfer(ctx, audit.Item.ID, status, audit.Paths, audit.Bytes, audit.ErrorText)
|
err = audit.Store.FinishSSHFileTransfer(ctx, audit.Item.ID, status, audit.Paths, audit.Bytes, audit.ErrorText)
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ import "codit/internal/models"
|
|||||||
|
|
||||||
const logIDSSHBroker string = "ssh-broker"
|
const logIDSSHBroker string = "ssh-broker"
|
||||||
const sshWebsocketWriteTimeout time.Duration = 5 * time.Second
|
const sshWebsocketWriteTimeout time.Duration = 5 * time.Second
|
||||||
|
const sshHostKeyDiscoveryTimeout time.Duration = 10 * time.Second
|
||||||
|
const sshSessionDialTimeout time.Duration = 15 * time.Second
|
||||||
const sshSessionInputBufferSize int = 64
|
const sshSessionInputBufferSize int = 64
|
||||||
const sshPrepareStageLoadProfile string = "load_profile"
|
const sshPrepareStageLoadProfile string = "load_profile"
|
||||||
const sshPrepareStageLoadServer string = "load_server"
|
const sshPrepareStageLoadServer string = "load_server"
|
||||||
@@ -2536,9 +2538,9 @@ func discoverSSHServerHostKey(host string, port int, defaultUser string) (models
|
|||||||
discovered = key
|
discovered = key
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
Timeout: 10 * time.Second,
|
Timeout: sshHostKeyDiscoveryTimeout,
|
||||||
}
|
}
|
||||||
conn, err = net.DialTimeout("tcp", addr, 10*time.Second)
|
conn, err = net.DialTimeout("tcp", addr, sshHostKeyDiscoveryTimeout)
|
||||||
if err != nil { return item, err }
|
if err != nil { return item, err }
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
@@ -3072,7 +3074,7 @@ func (api *API) runSSHSessionStream(ctx context.Context, store *db.Store, user *
|
|||||||
Auth: authMethods,
|
Auth: authMethods,
|
||||||
HostKeyCallback: sshHostKeyCallback(hostKeys, &connectedFingerprint, &connectedHostKey, prepared.PinHostKeyOnFirstUse),
|
HostKeyCallback: sshHostKeyCallback(hostKeys, &connectedFingerprint, &connectedHostKey, prepared.PinHostKeyOnFirstUse),
|
||||||
}
|
}
|
||||||
dialCtx, dialCancel = context.WithTimeout(ctx, 15*time.Second)
|
dialCtx, dialCancel = context.WithTimeout(ctx, sshSessionDialTimeout)
|
||||||
runtimeSession.SetDialCancel(dialCancel)
|
runtimeSession.SetDialCancel(dialCancel)
|
||||||
defer dialCancel()
|
defer dialCancel()
|
||||||
dialConn, err = (&net.Dialer{}).DialContext(dialCtx, "tcp", dialAddr)
|
dialConn, err = (&net.Dialer{}).DialContext(dialCtx, "tcp", dialAddr)
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ func (m *MirrorManager) runDue(ctx context.Context) {
|
|||||||
|
|
||||||
for i = 0; i < len(tasks); i++ {
|
for i = 0; i < len(tasks); i++ {
|
||||||
// use an independent bounded context so task cancellation does not skip the state transition.
|
// use an independent bounded context so task cancellation does not skip the state transition.
|
||||||
writeCtx, writeCancel = context.WithTimeout(context.Background(), 10 * time.Second)
|
writeCtx, writeCancel = context.WithTimeout(context.Background(), db.IndependentDBOperationTimeout)
|
||||||
runID, started, err = m.store.TryStartRPMMirrorRun(writeCtx, tasks[i].RepoID, tasks[i].MirrorPath, now)
|
runID, started, err = m.store.TryStartRPMMirrorRun(writeCtx, tasks[i].RepoID, tasks[i].MirrorPath, now)
|
||||||
writeCancel()
|
writeCancel()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -358,7 +358,7 @@ func (m *MirrorManager) finishTaskRun(task models.RPMMirrorTask, runID string, s
|
|||||||
finishedAt = time.Now().UTC().Unix()
|
finishedAt = time.Now().UTC().Unix()
|
||||||
|
|
||||||
// use a child of context.Background() to write the status regardless of task cancellation
|
// use a child of context.Background() to write the status regardless of task cancellation
|
||||||
writeCtx, writeCancel = context.WithTimeout(context.Background(), 10 * time.Second); defer writeCancel()
|
writeCtx, writeCancel = context.WithTimeout(context.Background(), db.IndependentDBOperationTimeout); defer writeCancel()
|
||||||
err = m.store.FinishRPMMirrorTaskRun(writeCtx, task.RepoID, task.MirrorPath, runID, success, finishedAt, step, total, done, failed, deleted, revision, errMsg)
|
err = m.store.FinishRPMMirrorTaskRun(writeCtx, task.RepoID, task.MirrorPath, runID, success, finishedAt, step, total, done, failed, deleted, revision, errMsg)
|
||||||
if err != nil && m.logger != nil {
|
if err != nil && m.logger != nil {
|
||||||
m.logger.Write(logIDRPMMirror, codit_logger.LOG_ERROR, "finish sync state failed repo=%s path=%s run=%s err=%v", task.RepoID, task.MirrorPath, runID, err)
|
m.logger.Write(logIDRPMMirror, codit_logger.LOG_ERROR, "finish sync state failed repo=%s path=%s run=%s err=%v", task.RepoID, task.MirrorPath, runID, err)
|
||||||
|
|||||||
+12
-10
@@ -40,6 +40,8 @@ import "golang.org/x/text/transform"
|
|||||||
import _ "modernc.org/sqlite"
|
import _ "modernc.org/sqlite"
|
||||||
|
|
||||||
const logIDAPI string = "api"
|
const logIDAPI string = "api"
|
||||||
|
const serverShutdownTimeout time.Duration = 10 * time.Second
|
||||||
|
const httpServerShutdownTimeout time.Duration = 5 * time.Second
|
||||||
|
|
||||||
type server_http_log_writer struct {
|
type server_http_log_writer struct {
|
||||||
l Logger
|
l Logger
|
||||||
@@ -653,7 +655,7 @@ func (app *Server) ServeContext(ctx context.Context) error {
|
|||||||
err = extraListenerManager.Start()
|
err = extraListenerManager.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
app.logger.Write("", LOG_ERROR, "additional listener manager error - %v", err)
|
app.logger.Write("", LOG_ERROR, "additional listener manager error - %v", err)
|
||||||
shutdownCtx, cancel = context.WithTimeout(context.Background(), 10*time.Second)
|
shutdownCtx, cancel = context.WithTimeout(context.Background(), serverShutdownTimeout)
|
||||||
extraListenerManager.Stop(shutdownCtx)
|
extraListenerManager.Stop(shutdownCtx)
|
||||||
app.rpm_mirror.Stop()
|
app.rpm_mirror.Stop()
|
||||||
app.rpm_mirror.Wait()
|
app.rpm_mirror.Wait()
|
||||||
@@ -664,7 +666,7 @@ func (app *Server) ServeContext(ctx context.Context) error {
|
|||||||
mainEndpoints, err = buildListenerEndpoints("main", "main", "", util.TLSSettingsFromConfig(*app.cfg), app.store)
|
mainEndpoints, err = buildListenerEndpoints("main", "main", "", util.TLSSettingsFromConfig(*app.cfg), app.store)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
app.logger.Write("", LOG_ERROR, "main listener config error - %v", err)
|
app.logger.Write("", LOG_ERROR, "main listener config error - %v", err)
|
||||||
shutdownCtx, cancel = context.WithTimeout(context.Background(), 10*time.Second)
|
shutdownCtx, cancel = context.WithTimeout(context.Background(), serverShutdownTimeout)
|
||||||
extraListenerManager.Stop(shutdownCtx)
|
extraListenerManager.Stop(shutdownCtx)
|
||||||
app.rpm_mirror.Stop()
|
app.rpm_mirror.Stop()
|
||||||
app.rpm_mirror.Wait()
|
app.rpm_mirror.Wait()
|
||||||
@@ -674,7 +676,7 @@ func (app *Server) ServeContext(ctx context.Context) error {
|
|||||||
if len(mainEndpoints) == 0 && extraListenerManager.TotalEndpointCount() == 0 {
|
if len(mainEndpoints) == 0 && extraListenerManager.TotalEndpointCount() == 0 {
|
||||||
err = errors.New("at least one main or additional listener is required")
|
err = errors.New("at least one main or additional listener is required")
|
||||||
app.logger.Write("", LOG_ERROR, "listener config error - %v", err)
|
app.logger.Write("", LOG_ERROR, "listener config error - %v", err)
|
||||||
shutdownCtx, cancel = context.WithTimeout(context.Background(), 10*time.Second)
|
shutdownCtx, cancel = context.WithTimeout(context.Background(), serverShutdownTimeout)
|
||||||
extraListenerManager.Stop(shutdownCtx)
|
extraListenerManager.Stop(shutdownCtx)
|
||||||
app.rpm_mirror.Stop()
|
app.rpm_mirror.Stop()
|
||||||
app.rpm_mirror.Wait()
|
app.rpm_mirror.Wait()
|
||||||
@@ -683,7 +685,7 @@ func (app *Server) ServeContext(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
if len(mainEndpoints) == 0 {
|
if len(mainEndpoints) == 0 {
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
shutdownCtx, cancel = context.WithTimeout(context.Background(), 10*time.Second)
|
shutdownCtx, cancel = context.WithTimeout(context.Background(), serverShutdownTimeout)
|
||||||
extraListenerManager.Stop(shutdownCtx)
|
extraListenerManager.Stop(shutdownCtx)
|
||||||
if servicesStarted && app.rpm_mirror != nil {
|
if servicesStarted && app.rpm_mirror != nil {
|
||||||
app.rpm_mirror.Stop()
|
app.rpm_mirror.Stop()
|
||||||
@@ -693,7 +695,7 @@ func (app *Server) ServeContext(ctx context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
err = serveListeners(ctx, mainEndpoints, mux, app.logger, app.serverId)
|
err = serveListeners(ctx, mainEndpoints, mux, app.logger, app.serverId)
|
||||||
shutdownCtx, cancel = context.WithTimeout(context.Background(), 10*time.Second)
|
shutdownCtx, cancel = context.WithTimeout(context.Background(), serverShutdownTimeout)
|
||||||
if extraListenerManager != nil {
|
if extraListenerManager != nil {
|
||||||
extraListenerManager.Stop(shutdownCtx)
|
extraListenerManager.Stop(shutdownCtx)
|
||||||
}
|
}
|
||||||
@@ -826,7 +828,7 @@ func (app *Server) initHandlers() (*handlers.API, *http.ServeMux, error) {
|
|||||||
router.Handle("GET", "/api/admin/pki/certs", txRead(api.ListPKICerts))
|
router.Handle("GET", "/api/admin/pki/certs", txRead(api.ListPKICerts))
|
||||||
router.Handle("GET", "/api/admin/pki/certs/:id", txRead(api.GetPKICert))
|
router.Handle("GET", "/api/admin/pki/certs/:id", txRead(api.GetPKICert))
|
||||||
router.Handle("GET", "/api/admin/pki/certs/:id/inspect", txRead(api.GetPKICertInspect))
|
router.Handle("GET", "/api/admin/pki/certs/:id/inspect", txRead(api.GetPKICertInspect))
|
||||||
router.Handle("POST", "/api/admin/pki/certs/:id/renew-acme", txWrite(api.RenewPKICertWithACME))
|
router.Handle("POST", "/api/admin/pki/certs/:id/renew-acme", api.RenewPKICertWithACME)
|
||||||
router.Handle("GET", "/api/admin/pki/certs/:id/bundle", api.DownloadPKICertBundle)
|
router.Handle("GET", "/api/admin/pki/certs/:id/bundle", api.DownloadPKICertBundle)
|
||||||
router.Handle("POST", "/api/admin/pki/certs", txWrite(api.IssuePKICert))
|
router.Handle("POST", "/api/admin/pki/certs", txWrite(api.IssuePKICert))
|
||||||
router.Handle("POST", "/api/admin/pki/certs/import", txWrite(api.ImportPKICert))
|
router.Handle("POST", "/api/admin/pki/certs/import", txWrite(api.ImportPKICert))
|
||||||
@@ -841,7 +843,7 @@ func (app *Server) initHandlers() (*handlers.API, *http.ServeMux, error) {
|
|||||||
router.Handle("PATCH", "/api/admin/pki/acme/profiles/:id", txWrite(api.UpdateACMEProfile))
|
router.Handle("PATCH", "/api/admin/pki/acme/profiles/:id", txWrite(api.UpdateACMEProfile))
|
||||||
router.Handle("DELETE", "/api/admin/pki/acme/profiles/:id", txWrite(api.DeleteACMEProfile))
|
router.Handle("DELETE", "/api/admin/pki/acme/profiles/:id", txWrite(api.DeleteACMEProfile))
|
||||||
router.Handle("GET", "/api/admin/pki/acme/orders", txRead(api.ListACMEOrders))
|
router.Handle("GET", "/api/admin/pki/acme/orders", txRead(api.ListACMEOrders))
|
||||||
router.Handle("POST", "/api/admin/pki/acme/orders", txWrite(api.CreateACMEOrder))
|
router.Handle("POST", "/api/admin/pki/acme/orders", api.CreateACMEOrder)
|
||||||
router.Handle("POST", "/api/admin/pki/acme/orders/:id/finalize", api.FinalizeACMEOrder)
|
router.Handle("POST", "/api/admin/pki/acme/orders/:id/finalize", api.FinalizeACMEOrder)
|
||||||
router.Handle("DELETE", "/api/admin/pki/acme/orders/:id", txWrite(api.DeleteACMEOrder))
|
router.Handle("DELETE", "/api/admin/pki/acme/orders/:id", txWrite(api.DeleteACMEOrder))
|
||||||
router.Handle("GET", "/api/admin/rpm/mirrors", txRead(api.ListAdminRPMMirrors))
|
router.Handle("GET", "/api/admin/rpm/mirrors", txRead(api.ListAdminRPMMirrors))
|
||||||
@@ -1253,7 +1255,7 @@ func (m *additionalListenerManager) Stop(ctx context.Context) {
|
|||||||
|
|
||||||
m.mu.Lock()
|
m.mu.Lock()
|
||||||
for key, running = range m.Running {
|
for key, running = range m.Running {
|
||||||
shutdownCtx, cancel = context.WithTimeout(context.Background(), 5*time.Second)
|
shutdownCtx, cancel = context.WithTimeout(context.Background(), httpServerShutdownTimeout)
|
||||||
_ = running.Server.Shutdown(shutdownCtx)
|
_ = running.Server.Shutdown(shutdownCtx)
|
||||||
cancel()
|
cancel()
|
||||||
delete(m.Running, key)
|
delete(m.Running, key)
|
||||||
@@ -1340,7 +1342,7 @@ func (m *additionalListenerManager) reconcile() error {
|
|||||||
if exists {
|
if exists {
|
||||||
continue
|
continue
|
||||||
} // found in configuration
|
} // found in configuration
|
||||||
shutdownCtx, cancel = context.WithTimeout(context.Background(), 5*time.Second)
|
shutdownCtx, cancel = context.WithTimeout(context.Background(), httpServerShutdownTimeout)
|
||||||
_ = running.Server.Shutdown(shutdownCtx)
|
_ = running.Server.Shutdown(shutdownCtx)
|
||||||
cancel()
|
cancel()
|
||||||
delete(m.Running, key)
|
delete(m.Running, key)
|
||||||
@@ -2136,7 +2138,7 @@ func serveListeners(ctx context.Context, endpoints []listenerEndpoint, handler h
|
|||||||
copied = append(copied, servers...)
|
copied = append(copied, servers...)
|
||||||
serversMu.Unlock()
|
serversMu.Unlock()
|
||||||
for _, server = range copied {
|
for _, server = range copied {
|
||||||
shutdownCtx, shutdownCancel = context.WithTimeout(context.Background(), 5*time.Second)
|
shutdownCtx, shutdownCancel = context.WithTimeout(context.Background(), httpServerShutdownTimeout)
|
||||||
_ = server.Shutdown(shutdownCtx)
|
_ = server.Shutdown(shutdownCtx)
|
||||||
shutdownCancel()
|
shutdownCancel()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user