Compare commits

...

4 Commits

7 changed files with 309 additions and 169 deletions
+8 -1
View File
@@ -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
} }
+114 -46
View File
@@ -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
} }
+167 -106
View File
@@ -2207,40 +2207,53 @@ 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})
}
running, err = api.store(r).HasRunningRPMMirrorTask(repos[i].ID)
if err != nil {
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
return return
} }
if running {
WriteJSON(w, http.StatusConflict, map[string]string{"error": "cannot delete project while rpm mirror sync is running", "repo_id": repos[i].ID, "repo_name": repos[i].Name}) if repos[i].Type == models.RepoTypeRPM {
return var running bool
running, err = api.store(r).HasRunningRPMMirrorTask(repos[i].ID)
if err != nil {
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
return
}
if running {
WriteJSON(w, http.StatusConflict, map[string]string{"error": "cannot delete project while rpm mirror sync is running", "repo_id": repos[i].ID, "repo_name": repos[i].Name})
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,22 +2283,29 @@ 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
}
running, err = txStore.HasRunningRPMMirrorTask(currentRepos[i].ID)
if err != nil {
_ = txStore.Rollback() _ = txStore.Rollback()
restoreFromTrash(tempPaths, sourcePaths) restoreFromTrash(tempPaths, sourcePaths)
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error()) WriteJSON(w, http.StatusConflict, map[string]string{"error": "repository storage is not ready", "repo_id": currentRepos[i].ID, "repo_name": currentRepos[i].Name})
return return
} }
if running { if currentRepos[i].Type == models.RepoTypeRPM {
_ = txStore.Rollback() var running bool
restoreFromTrash(tempPaths, sourcePaths) running, err = txStore.HasRunningRPMMirrorTask(currentRepos[i].ID)
WriteJSON(w, http.StatusConflict, map[string]string{"error": "cannot delete project while rpm mirror sync is running", "repo_id": currentRepos[i].ID, "repo_name": currentRepos[i].Name}) if err != nil {
return _ = txStore.Rollback()
restoreFromTrash(tempPaths, sourcePaths)
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
return
}
if running {
_ = txStore.Rollback()
restoreFromTrash(tempPaths, sourcePaths)
WriteJSON(w, http.StatusConflict, map[string]string{"error": "cannot delete project while rpm mirror sync is running", "repo_id": currentRepos[i].ID, "repo_name": currentRepos[i].Name})
return
}
} }
} }
err = txStore.DeleteProject(params["id"]) err = txStore.DeleteProject(params["id"])
@@ -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)
+5 -3
View File
@@ -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)
+2 -2
View File
@@ -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
View File
@@ -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()
} }