mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-11-26 11:51:02 +00:00
Add agent no-schedule flag (#1567)
This flag allows an agent owner / admin to stop the agent from taking new workflows / pipelines.
This commit is contained in:
parent
5f818e933b
commit
71d6c03ca7
7 changed files with 28 additions and 19 deletions
|
@ -73,6 +73,7 @@ func PatchAgent(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
agent.Name = in.Name
|
agent.Name = in.Name
|
||||||
|
agent.NoSchedule = in.NoSchedule
|
||||||
|
|
||||||
err = _store.AgentUpdate(agent)
|
err = _store.AgentUpdate(agent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -96,6 +97,7 @@ func PostAgent(c *gin.Context) {
|
||||||
|
|
||||||
agent := &model.Agent{
|
agent := &model.Agent{
|
||||||
Name: in.Name,
|
Name: in.Name,
|
||||||
|
NoSchedule: in.NoSchedule,
|
||||||
OwnerID: user.ID,
|
OwnerID: user.ID,
|
||||||
Token: base32.StdEncoding.EncodeToString(
|
Token: base32.StdEncoding.EncodeToString(
|
||||||
securecookie.GenerateRandomKey(32),
|
securecookie.GenerateRandomKey(32),
|
||||||
|
|
|
@ -70,6 +70,13 @@ func (s *RPC) Next(c context.Context, agentFilter rpc.Filter) (*rpc.Pipeline, er
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
|
agent, err := s.getAgentFromContext(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if agent.NoSchedule {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
task, err := s.queue.Poll(c, fn)
|
task, err := s.queue.Poll(c, fn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -82,6 +89,7 @@ func (s *RPC) Next(c context.Context, agentFilter rpc.Filter) (*rpc.Pipeline, er
|
||||||
err = json.Unmarshal(task.Data, pipeline)
|
err = json.Unmarshal(task.Data, pipeline)
|
||||||
return pipeline, err
|
return pipeline, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.Done(c, task.ID, rpc.State{}); err != nil {
|
if err := s.Done(c, task.ID, rpc.State{}); err != nil {
|
||||||
log.Error().Err(err).Msgf("mark task '%s' done failed", task.ID)
|
log.Error().Err(err).Msgf("mark task '%s' done failed", task.ID)
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ type Agent struct {
|
||||||
Backend string `json:"backend" xorm:"VARCHAR(100)"`
|
Backend string `json:"backend" xorm:"VARCHAR(100)"`
|
||||||
Capacity int32 `json:"capacity"`
|
Capacity int32 `json:"capacity"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
|
NoSchedule bool `json:"no_schedule"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName return database table name for xorm
|
// TableName return database table name for xorm
|
||||||
|
|
18
web/components.d.ts
vendored
18
web/components.d.ts
vendored
|
@ -21,10 +21,8 @@ declare module '@vue/runtime-core' {
|
||||||
FluidContainer: typeof import('./src/components/layout/FluidContainer.vue')['default']
|
FluidContainer: typeof import('./src/components/layout/FluidContainer.vue')['default']
|
||||||
GeneralTab: typeof import('./src/components/repo/settings/GeneralTab.vue')['default']
|
GeneralTab: typeof import('./src/components/repo/settings/GeneralTab.vue')['default']
|
||||||
Header: typeof import('./src/components/layout/scaffold/Header.vue')['default']
|
Header: typeof import('./src/components/layout/scaffold/Header.vue')['default']
|
||||||
IBiCheckCircle: typeof import('~icons/bi/check-circle')['default']
|
|
||||||
IBiCheckCircleFill: typeof import('~icons/bi/check-circle-fill')['default']
|
IBiCheckCircleFill: typeof import('~icons/bi/check-circle-fill')['default']
|
||||||
IBiCircle: typeof import('~icons/bi/circle')['default']
|
IBiCircle: typeof import('~icons/bi/circle')['default']
|
||||||
IBiPauseCircleFill: typeof import('~icons/bi/pause-circle-fill')['default']
|
|
||||||
IBiPlayCircleFill: typeof import('~icons/bi/play-circle-fill')['default']
|
IBiPlayCircleFill: typeof import('~icons/bi/play-circle-fill')['default']
|
||||||
IBiSlashCircleFill: typeof import('~icons/bi/slash-circle-fill')['default']
|
IBiSlashCircleFill: typeof import('~icons/bi/slash-circle-fill')['default']
|
||||||
IBiStopCircleFill: typeof import('~icons/bi/stop-circle-fill')['default']
|
IBiStopCircleFill: typeof import('~icons/bi/stop-circle-fill')['default']
|
||||||
|
@ -33,13 +31,9 @@ declare module '@vue/runtime-core' {
|
||||||
ICarbonCloseOutline: typeof import('~icons/carbon/close-outline')['default']
|
ICarbonCloseOutline: typeof import('~icons/carbon/close-outline')['default']
|
||||||
ICarbonInProgress: typeof import('~icons/carbon/in-progress')['default']
|
ICarbonInProgress: typeof import('~icons/carbon/in-progress')['default']
|
||||||
IClarityDeployLine: typeof import('~icons/clarity/deploy-line')['default']
|
IClarityDeployLine: typeof import('~icons/clarity/deploy-line')['default']
|
||||||
IClarityNoAccessSolid: typeof import('~icons/clarity/no-access-solid')['default']
|
|
||||||
IClaritySettingsSolid: typeof import('~icons/clarity/settings-solid')['default']
|
IClaritySettingsSolid: typeof import('~icons/clarity/settings-solid')['default']
|
||||||
IClaritySuccessStandardSolid: typeof import('~icons/clarity/success-standard-solid')['default']
|
|
||||||
IClarityTimesCircleSolid: typeof import('~icons/clarity/times-circle-solid')['default']
|
|
||||||
Icon: typeof import('./src/components/atomic/Icon.vue')['default']
|
Icon: typeof import('./src/components/atomic/Icon.vue')['default']
|
||||||
IconButton: typeof import('./src/components/atomic/IconButton.vue')['default']
|
IconButton: typeof import('./src/components/atomic/IconButton.vue')['default']
|
||||||
IEntypoDotsTwoVertical: typeof import('~icons/entypo/dots-two-vertical')['default']
|
|
||||||
IGgTrash: typeof import('~icons/gg/trash')['default']
|
IGgTrash: typeof import('~icons/gg/trash')['default']
|
||||||
IIcBaselineDarkMode: typeof import('~icons/ic/baseline-dark-mode')['default']
|
IIcBaselineDarkMode: typeof import('~icons/ic/baseline-dark-mode')['default']
|
||||||
IIcBaselineDownloadForOffline: typeof import('~icons/ic/baseline-download-for-offline')['default']
|
IIcBaselineDownloadForOffline: typeof import('~icons/ic/baseline-download-for-offline')['default']
|
||||||
|
@ -67,24 +61,14 @@ declare module '@vue/runtime-core' {
|
||||||
IMdiSync: typeof import('~icons/mdi/sync')['default']
|
IMdiSync: typeof import('~icons/mdi/sync')['default']
|
||||||
IMdiTagOutline: typeof import('~icons/mdi/tag-outline')['default']
|
IMdiTagOutline: typeof import('~icons/mdi/tag-outline')['default']
|
||||||
InputField: typeof import('./src/components/form/InputField.vue')['default']
|
InputField: typeof import('./src/components/form/InputField.vue')['default']
|
||||||
IOcticonSkip24: typeof import('~icons/octicon/skip24')['default']
|
|
||||||
IPhCheckCircle: typeof import('~icons/ph/check-circle')['default']
|
|
||||||
IPhGitlabLogoSimpleFill: typeof import('~icons/ph/gitlab-logo-simple-fill')['default']
|
IPhGitlabLogoSimpleFill: typeof import('~icons/ph/gitlab-logo-simple-fill')['default']
|
||||||
IPhHand: typeof import('~icons/ph/hand')['default']
|
|
||||||
IPhHourglass: typeof import('~icons/ph/hourglass')['default']
|
|
||||||
IPhProhibit: typeof import('~icons/ph/prohibit')['default']
|
|
||||||
IPhWarning: typeof import('~icons/ph/warning')['default']
|
|
||||||
IPhXCircle: typeof import('~icons/ph/x-circle')['default']
|
|
||||||
ISimpleIconsGitea: typeof import('~icons/simple-icons/gitea')['default']
|
ISimpleIconsGitea: typeof import('~icons/simple-icons/gitea')['default']
|
||||||
ITeenyiconsGitSolid: typeof import('~icons/teenyicons/git-solid')['default']
|
ITeenyiconsGitSolid: typeof import('~icons/teenyicons/git-solid')['default']
|
||||||
IUiwCircleCheck: typeof import('~icons/uiw/circle-check')['default']
|
ITeenyiconsRefreshOutline: typeof import('~icons/teenyicons/refresh-outline')['default']
|
||||||
IUiwCircleClose: typeof import('~icons/uiw/circle-close')['default']
|
|
||||||
IUiwStop: typeof import('~icons/uiw/stop')['default']
|
|
||||||
IVaadinQuestionCircleO: typeof import('~icons/vaadin/question-circle-o')['default']
|
IVaadinQuestionCircleO: typeof import('~icons/vaadin/question-circle-o')['default']
|
||||||
ListItem: typeof import('./src/components/atomic/ListItem.vue')['default']
|
ListItem: typeof import('./src/components/atomic/ListItem.vue')['default']
|
||||||
ManualPipelinePopup: typeof import('./src/components/layout/popups/ManualPipelinePopup.vue')['default']
|
ManualPipelinePopup: typeof import('./src/components/layout/popups/ManualPipelinePopup.vue')['default']
|
||||||
Navbar: typeof import('./src/components/layout/header/Navbar.vue')['default']
|
Navbar: typeof import('./src/components/layout/header/Navbar.vue')['default']
|
||||||
NavbarIcon: typeof import('./src/components/layout/header/NavbarIcon.vue')['default']
|
|
||||||
NumberField: typeof import('./src/components/form/NumberField.vue')['default']
|
NumberField: typeof import('./src/components/form/NumberField.vue')['default']
|
||||||
OrgSecretsTab: typeof import('./src/components/org/settings/OrgSecretsTab.vue')['default']
|
OrgSecretsTab: typeof import('./src/components/org/settings/OrgSecretsTab.vue')['default']
|
||||||
Panel: typeof import('./src/components/layout/Panel.vue')['default']
|
Panel: typeof import('./src/components/layout/Panel.vue')['default']
|
||||||
|
|
|
@ -336,6 +336,10 @@
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"placeholder": "Name of the agent"
|
"placeholder": "Name of the agent"
|
||||||
},
|
},
|
||||||
|
"no_schedule": {
|
||||||
|
"name": "Disable agent",
|
||||||
|
"placeholder": "Stop agent from taking new tasks"
|
||||||
|
},
|
||||||
"token": "Token",
|
"token": "Token",
|
||||||
"platform": "Platform",
|
"platform": "Platform",
|
||||||
"backend": "Backend",
|
"backend": "Backend",
|
||||||
|
|
|
@ -43,6 +43,14 @@
|
||||||
/>
|
/>
|
||||||
</InputField>
|
</InputField>
|
||||||
|
|
||||||
|
<InputField :label="$t('admin.settings.agents.no_schedule.name')">
|
||||||
|
<Checkbox
|
||||||
|
:model-value="selectedAgent.no_schedule || false"
|
||||||
|
:label="$t('admin.settings.agents.no_schedule.placeholder')"
|
||||||
|
@update:model-value="selectedAgent!.no_schedule = $event"
|
||||||
|
/>
|
||||||
|
</InputField>
|
||||||
|
|
||||||
<template v-if="isEditingAgent">
|
<template v-if="isEditingAgent">
|
||||||
<InputField :label="$t('admin.settings.agents.token')">
|
<InputField :label="$t('admin.settings.agents.token')">
|
||||||
<TextField v-model="selectedAgent.token" :placeholder="$t('admin.settings.agents.token')" disabled />
|
<TextField v-model="selectedAgent.token" :placeholder="$t('admin.settings.agents.token')" disabled />
|
||||||
|
@ -97,6 +105,7 @@ import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import Button from '~/components/atomic/Button.vue';
|
import Button from '~/components/atomic/Button.vue';
|
||||||
import ListItem from '~/components/atomic/ListItem.vue';
|
import ListItem from '~/components/atomic/ListItem.vue';
|
||||||
|
import Checkbox from '~/components/form/Checkbox.vue';
|
||||||
import InputField from '~/components/form/InputField.vue';
|
import InputField from '~/components/form/InputField.vue';
|
||||||
import TextField from '~/components/form/TextField.vue';
|
import TextField from '~/components/form/TextField.vue';
|
||||||
import Panel from '~/components/layout/Panel.vue';
|
import Panel from '~/components/layout/Panel.vue';
|
||||||
|
|
|
@ -9,4 +9,5 @@ export type Agent = {
|
||||||
backend: string;
|
backend: string;
|
||||||
capacity: number;
|
capacity: number;
|
||||||
version: string;
|
version: string;
|
||||||
|
no_schedule: boolean;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue