feat: extend the p2p preheat policy

Add the field extra_attrs to the p2p preheat policy for the provider to
define their specified parameters when preheating.

Signed-off-by: chlins <chlins.zhang@gmail.com>
This commit is contained in:
chlins 2024-10-30 16:02:30 +08:00
parent 91082af39f
commit 8c21bc8e22
23 changed files with 2235 additions and 2069 deletions

View File

@ -7098,6 +7098,9 @@ definitions:
scope:
type: string
description: The scope of preheat policy
extra_attrs:
type: string
description: The extra attributes of preheat policy
creation_time:
type: string
format: date-time

View File

@ -0,0 +1 @@
ALTER TABLE p2p_preheat_policy ADD COLUMN IF NOT EXISTS extra_attrs json;

View File

@ -402,7 +402,7 @@ func (de *defaultEnforcer) launchExecutions(ctx context.Context, candidates []*s
// Start tasks
count := 0
for _, c := range candidates {
if _, err = de.startTask(ctx, eid, c, insData, pl.Scope); err != nil {
if _, err = de.startTask(ctx, eid, c, insData, pl.Scope, pl.ExtraAttrs); err != nil {
// Just log the error and skip
log.Errorf("start task error for preheating image: %s/%s:%s@%s", c.Namespace, c.Repository, c.Tags[0], c.Digest)
continue
@ -421,7 +421,7 @@ func (de *defaultEnforcer) launchExecutions(ctx context.Context, candidates []*s
}
// startTask starts the preheat task(job) for the given candidate
func (de *defaultEnforcer) startTask(ctx context.Context, executionID int64, candidate *selector.Candidate, instance, scope string) (int64, error) {
func (de *defaultEnforcer) startTask(ctx context.Context, executionID int64, candidate *selector.Candidate, instance, scope string, extraAttrs map[string]interface{}) (int64, error) {
u, err := de.fullURLGetter(candidate)
if err != nil {
return -1, err
@ -442,6 +442,7 @@ func (de *defaultEnforcer) startTask(ctx context.Context, executionID int64, can
Tag: candidate.Tags[0],
Digest: candidate.Digest,
Scope: scope,
ExtraAttrs: extraAttrs,
}
piData, err := pi.ToJSON()

View File

@ -82,6 +82,9 @@ type Schema struct {
Enabled bool `orm:"column(enabled)" json:"enabled"`
// Scope decides the preheat scope.
Scope string `orm:"column(scope)" json:"scope"`
// ExtraAttrs is used to store extra attributes provided by vendor.
ExtraAttrsStr string `orm:"column(extra_attrs)" json:"-"`
ExtraAttrs map[string]interface{} `orm:"-" json:"extra_attrs"`
CreatedAt time.Time `orm:"column(creation_time)" json:"creation_time"`
UpdatedTime time.Time `orm:"column(update_time)" json:"update_time"`
}
@ -162,6 +165,14 @@ func (s *Schema) Encode() error {
s.TriggerStr = string(triggerStr)
}
if s.ExtraAttrs != nil {
extraAttrsStr, err := json.Marshal(s.ExtraAttrs)
if err != nil {
return err
}
s.ExtraAttrsStr = string(extraAttrsStr)
}
return nil
}
@ -181,6 +192,13 @@ func (s *Schema) Decode() error {
}
s.Trigger = trigger
// parse extra attributes
extraAttrs, err := decodeExtraAttrs(s.ExtraAttrsStr)
if err != nil {
return err
}
s.ExtraAttrs = extraAttrs
return nil
}
@ -230,3 +248,17 @@ func decodeTrigger(triggerStr string) (*Trigger, error) {
return trigger, nil
}
// decodeExtraAttrs parse extraAttrsStr to extraAttrs.
func decodeExtraAttrs(extraAttrsStr string) (map[string]interface{}, error) {
if len(extraAttrsStr) == 0 {
return nil, nil
}
extraAttrs := make(map[string]interface{})
if err := json.Unmarshal([]byte(extraAttrsStr), &extraAttrs); err != nil {
return nil, err
}
return extraAttrs, nil
}

View File

@ -87,12 +87,14 @@ func (p *PolicyTestSuite) TestDecode() {
TriggerStr: "{\"type\":\"event_based\",\"trigger_setting\":{\"cron\":\"\"}}",
Enabled: false,
Scope: "all_peers",
ExtraAttrsStr: "{\"key\":\"value\"}",
}
p.NoError(s.Decode())
p.Len(s.Filters, 3)
p.NotNil(s.Trigger)
p.Equal(ScopeTypeAllPeers, s.Scope)
p.Equal(map[string]interface{}{"key": "value"}, s.ExtraAttrs)
// invalid filter or trigger
s.FiltersStr = ""
@ -133,9 +135,13 @@ func (p *PolicyTestSuite) TestEncode() {
TriggerStr: "",
Enabled: false,
Scope: "single_peer",
ExtraAttrs: map[string]interface{}{
"key": "value",
},
}
p.NoError(s.Encode())
p.Equal(`[{"type":"repository","value":"**"},{"type":"tag","value":"**"},{"type":"label","value":"test"}]`, s.FiltersStr)
p.Equal(`{"type":"event_based","trigger_setting":{}}`, s.TriggerStr)
p.Equal(ScopeTypeSinglePeer, s.Scope)
p.Equal(`{"key":"value"}`, s.ExtraAttrsStr)
}

View File

@ -54,13 +54,13 @@ const (
type dragonflyCreateJobRequest struct {
// Type is the job type, support preheat.
Type string `json:"type" binding:"required"`
Type string `json:"type"`
// Args is the preheating args.
Args dragonflyCreateJobRequestArgs `json:"args" binding:"omitempty"`
Args dragonflyCreateJobRequestArgs `json:"args"`
// SchedulerClusterIDs is the scheduler cluster ids for preheating.
SchedulerClusterIDs []uint `json:"scheduler_cluster_ids" binding:"omitempty"`
SchedulerClusterIDs []uint `json:"scheduler_cluster_ids"`
}
type dragonflyCreateJobRequestArgs struct {
@ -150,6 +150,12 @@ type dragonflyJobResponse struct {
} `json:"result"`
}
// dragonflyExtraAttrs is the extra attributes model definition for dragonfly provider.
type dragonflyExtraAttrs struct {
// ClusterIDs is the cluster ids for dragonfly provider.
ClusterIDs []uint `json:"cluster_ids"`
}
// DragonflyDriver implements the provider driver interface for Alibaba dragonfly.
// More details, please refer to https://github.com/alibaba/Dragonfly
type DragonflyDriver struct {
@ -201,6 +207,18 @@ func (dd *DragonflyDriver) Preheat(preheatingImage *PreheatImage) (*PreheatingSt
return nil, errors.New("no image specified")
}
var extraAttrs dragonflyExtraAttrs
if preheatingImage.ExtraAttrs != nil && len(preheatingImage.ExtraAttrs) > 0 {
extraAttrsStr, err := json.Marshal(preheatingImage.ExtraAttrs)
if err != nil {
return nil, errors.Wrap(err, "failed to marshal extra attributes")
}
if err := json.Unmarshal(extraAttrsStr, &extraAttrs); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal extra attributes")
}
}
// Construct the preheat job request by the given parameters of the preheating image .
req := &dragonflyCreateJobRequest{
Type: "preheat",
@ -213,6 +231,11 @@ func (dd *DragonflyDriver) Preheat(preheatingImage *PreheatImage) (*PreheatingSt
},
}
// Set the cluster ids if it is specified.
if len(extraAttrs.ClusterIDs) > 0 {
req.SchedulerClusterIDs = extraAttrs.ClusterIDs
}
url := fmt.Sprintf("%s%s", strings.TrimSuffix(dd.instance.Endpoint, "/"), dragonflyJobPath)
data, err := client.GetHTTPClient(dd.instance.Insecure).Post(url, dd.getCred(), req, nil)
if err != nil {

View File

@ -86,6 +86,9 @@ func (suite *DragonflyTestSuite) TestPreheat() {
URL: "https://harbor.com",
Digest: "sha256:f3c97e3bd1e27393eb853a5c90b1132f2cda84336d5ba5d100c720dc98524c82",
Scope: "single_peer",
ExtraAttrs: map[string]interface{}{
"cluster_ids": []uint{1, 2, 3},
},
})
require.NoError(suite.T(), err, "preheat image")
suite.Equal(provider.PreheatingStatusPending, st.Status, "preheat status")

View File

@ -48,6 +48,9 @@ type PreheatImage struct {
// Scope indicates the preheat scope.
Scope string `json:"scope,omitempty"`
// ExtraAttrs contains extra attributes for the preheating image.
ExtraAttrs map[string]interface{} `json:"extra_attrs,omitempty"`
}
// FromJSON build preheating image from the given data.

View File

@ -481,6 +481,23 @@
</select>
</clr-select-container>
</div>
<!-- extra_attrs -->
<div class="clr-form-control">
<label class="clr-control-label width-6rem">{{
'P2P_PROVIDER.EXTRA_ATTRIBUTES' | translate
}}</label>
<div class="clr-control-container">
<textarea
autocomplete="off"
class="clr-textarea width-380"
type="text"
id="extraAttrs"
#ngExtraAttrs="ngModel"
[disabled]="loading"
[(ngModel)]="extraAttrs"
name="extraAttrs"></textarea>
</div>
</div>
</section>
</form>
<div class="mt-1 bottom-btn" *ngIf="!isEdit">

View File

@ -12,6 +12,7 @@ import { NgForm } from '@angular/forms';
import { OriginCron, ProjectService } from '../../../../shared/services';
import { CronScheduleComponent } from '../../../../shared/components/cron-schedule';
import { PreheatService } from '../../../../../../ng-swagger-gen/services/preheat.service';
import { ExtraAttrs } from '../../../../../../ng-swagger-gen/models/extra-attrs';
import {
debounceTime,
distinctUntilChanged,
@ -76,6 +77,7 @@ export class AddP2pPolicyComponent implements OnInit, OnDestroy {
labels: string;
triggerType: string = TRIGGER.MANUAL;
scope: string = SCOPE.SINGLE_PEER;
extraAttrs: string;
cron: string;
@ViewChild('policyForm', { static: true }) currentForm: NgForm;
loading: boolean = false;
@ -90,6 +92,8 @@ export class AddP2pPolicyComponent implements OnInit, OnDestroy {
originLabelsForEdit: string;
originTriggerTypeForEdit: string;
originCronForEdit: string;
originScopeForEdit: string;
originExtraAttrsForEdit: string;
@Input()
providers: ProviderUnderProject[] = [];
preventVul: boolean = false;
@ -309,6 +313,7 @@ export class AddP2pPolicyComponent implements OnInit, OnDestroy {
this.loading = true;
this.buttonStatus = ClrLoadingState.LOADING;
policy.scope = this.scope ? this.scope : SCOPE.SINGLE_PEER;
policy.extra_attrs = this.extraAttrs;
deleteEmptyKey(policy);
if (isAdd) {
policy.project_id = this.projectId;
@ -410,7 +415,11 @@ export class AddP2pPolicyComponent implements OnInit, OnDestroy {
return true;
}
// eslint-disable-next-line eqeqeq
if (this.policy.scope != this.scope) {
if (this.originScopeForEdit != this.scope) {
return true;
}
// eslint-disable-next-line eqeqeq
if (this.originExtraAttrsForEdit != this.extraAttrs) {
return true;
}
// eslint-disable-next-line eqeqeq

View File

@ -166,6 +166,10 @@
<clr-dg-column>{{ 'P2P_PROVIDER.PROVIDER' | translate }}</clr-dg-column>
<clr-dg-column>{{ 'P2P_PROVIDER.FILTERS' | translate }}</clr-dg-column>
<clr-dg-column>{{ 'P2P_PROVIDER.TRIGGER' | translate }}</clr-dg-column>
<clr-dg-column>{{ 'P2P_PROVIDER.SCOPE' | translate }}</clr-dg-column>
<clr-dg-column>{{
'P2P_PROVIDER.EXTRA_ATTRIBUTES' | translate
}}</clr-dg-column>
<clr-dg-column [clrDgSortBy]="'creation_time'">{{
'P2P_PROVIDER.CREATED' | translate
}}</clr-dg-column>
@ -284,6 +288,29 @@
</clr-signpost-content>
</clr-signpost>
</clr-dg-cell>
<clr-dg-cell>{{ getScopeI18n(p.scope) | translate }}</clr-dg-cell>
<clr-dg-cell class="flex">
<clr-signpost>
<a class="btn btn-link link-normal" clrSignpostTrigger>
<span>
{{ toString(toJson(p?.extra_attrs)) }}
</span>
</a>
<clr-signpost-content
class="pre"
[clrPosition]="'top-middle'"
*clrIfOpen>
<hbr-copy-input
[iconMode]="true"
[defaultValue]="
toString(p?.extra_attrs)
"></hbr-copy-input>
<pre
[innerHTML]="toJson(p?.extra_attrs) | json"
class="abc"></pre>
</clr-signpost-content>
</clr-signpost>
</clr-dg-cell>
<clr-dg-cell>{{
p.creation_time | harborDatetime : 'short'
}}</clr-dg-cell>

View File

@ -58,3 +58,24 @@
.no-wrapper {
white-space: nowrap;
}
.flex {
display: flex;
}
.pre {
min-width: 25rem;
max-width: 40rem;
}
pre {
border: none;
}
.link-normal {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
max-width: 10rem;
text-transform: unset;
}

View File

@ -38,6 +38,7 @@ import {
TIME_OUT,
TRIGGER,
TRIGGER_I18N_MAP,
SCOPE_I18N_MAP,
} from '../p2p-provider.service';
import { PreheatPolicy } from '../../../../../../ng-swagger-gen/models/preheat-policy';
import { PreheatService } from '../../../../../../ng-swagger-gen/services/preheat.service';
@ -490,7 +491,8 @@ export class PolicyComponent implements OnInit, OnDestroy {
severity: this.addP2pPolicyComponent.severity,
label: this.addP2pPolicyComponent.labels,
triggerType: this.addP2pPolicyComponent.triggerType,
scope: this.addP2pPolicyComponent.scope,
scope: this.addP2pPolicyComponent.policy.scope,
extraAttrs: this.addP2pPolicyComponent.policy.extra_attrs,
});
this.addP2pPolicyComponent.originPolicyForEdit = clone(
this.selectedRow
@ -509,6 +511,10 @@ export class PolicyComponent implements OnInit, OnDestroy {
this.addP2pPolicyComponent.triggerType;
this.addP2pPolicyComponent.originCronForEdit =
this.addP2pPolicyComponent.cron;
this.addP2pPolicyComponent.originScopeForEdit =
this.addP2pPolicyComponent.scope;
this.addP2pPolicyComponent.originExtraAttrsForEdit =
this.addP2pPolicyComponent.extraAttrs;
}
}
@ -666,6 +672,13 @@ export class PolicyComponent implements OnInit, OnDestroy {
return trigger;
}
getScopeI18n(scope: string): string {
if (scope) {
return SCOPE_I18N_MAP[scope];
}
return '';
}
isScheduled(trigger: string): boolean {
return JSON.parse(trigger).type === TRIGGER.SCHEDULED;
}
@ -794,4 +807,17 @@ export class PolicyComponent implements OnInit, OnDestroy {
}
}
}
toString(v: any) {
if (v) {
return JSON.stringify(v);
}
return '';
}
toJson(v: any) {
if (v) {
return JSON.parse(v);
}
return null;
}
}

View File

@ -936,7 +936,6 @@
"LDAP_GROUP_MEMBERSHIP_INFO": "Dass Attribut, das die Mitglieder einer LDAP-Gruppe identifiziert. Standardwert ist memberof, in manchen LDAP Servern kann es \"ismemberof\" sein. Das Feld darf nicht leer sein, sofern eine LDAP Gruppen Funktion eingesetzt wird.",
"GROUP_SCOPE": "LDAP Gruppen Search Scope",
"GROUP_SCOPE_INFO": "Der Scope mit dem nach Gruppen gesucht wird. Standard ist Subtree."
},
"UAA": {
"ENDPOINT": "UAA Endpunkt",
@ -1310,7 +1309,6 @@
"ON": "am",
"AT": "um",
"NOSCHEDULE": "Ein Fehler ist aufgetreten beim setzen des Intervalls"
},
"GC": {
"CURRENT_SCHEDULE": "Speicherbereinigungs-Intervall",
@ -1636,6 +1634,7 @@
"SCOPE": "Umfang",
"SCOPE_SINGLE_PEER": "Einzelner Peer",
"SCOPE_ALL_PEERS": "Alle Peers",
"EXTRA_ATTRIBUTES": "Extra Attributes",
"NO_POLICY": "Keine Regelwerke",
"ENABLED_POLICY_SUMMARY": "Soll das Regelwerk {{name}} aktiviert werden?",
"DISABLED_POLICY_SUMMARY": "Soll das Regelwerk {{name}} deaktiviert werden?",

View File

@ -486,7 +486,6 @@
"CLOUD_EVENT": "CloudEvents",
"PAYLOAD_DATA": "Payload Data",
"SLACK_RATE_LIMIT": "Please be aware of Slack Rate Limits"
},
"GROUP": {
"GROUP": "Group",
@ -939,7 +938,6 @@
"GROUP_SCOPE_INFO": "The scope to search for groups, select Subtree by default.",
"GROUP_ATTACH_PARALLEL": "LDAP Group Attached In Parallel",
"GROUP_ATTACH_PARALLEL_INFO": "Enable this option to attach group in parallel to avoid timeout when there are too many groups. If disabled, the LDAP group information will be attached sequentially."
},
"UAA": {
"ENDPOINT": "UAA Endpoint",
@ -1313,7 +1311,6 @@
"ON": "on",
"AT": "at",
"NOSCHEDULE": "An error occurred in Get schedule"
},
"GC": {
"CURRENT_SCHEDULE": "Schedule to GC",
@ -1639,6 +1636,7 @@
"SCOPE": "Scope",
"SCOPE_SINGLE_PEER": "Single Peer",
"SCOPE_ALL_PEERS": "All Peers",
"EXTRA_ATTRIBUTES": "Extra Attributes",
"NO_POLICY": "No policy",
"ENABLED_POLICY_SUMMARY": "Do you want to enable policy {{name}}?",
"DISABLED_POLICY_SUMMARY": "Do you want to deactivate policy {{name}}?",

View File

@ -913,7 +913,6 @@
"WEEKLY_CRON": "Run once a week, midnight between Sat/Sun. Equivalente a 0 0 0 * * 0.",
"DAILY_CRON": "Run once a day, midnight. Equivalente a 0 0 0 * * *.",
"SKIP_SCANNER_PULL_TIME_TOOLTIP": "Vulnerability scanner(e.g. Trivy) will not update the image \"last pull time\" when the image is scanned."
},
"LDAP": {
"URL": "LDAP URL",
@ -1196,7 +1195,6 @@
"ADD_TAG": "ADD TAG",
"REMOVE_TAG": "REMOVE TAG",
"NAME_ALREADY_EXISTS": "Tag already exists under the repository"
},
"LABEL": {
"LABEL": "Label",
@ -1307,7 +1305,6 @@
"ON": "on",
"AT": "at",
"NOSCHEDULE": "An error occurred in Get schedule"
},
"GC": {
"CURRENT_SCHEDULE": "Schedule to GC",
@ -1633,6 +1630,7 @@
"SCOPE": "Scope",
"SCOPE_SINGLE_PEER": "Single Peer",
"SCOPE_ALL_PEERS": "All Peers",
"EXTRA_ATTRIBUTES": "Extra Attributes",
"NO_POLICY": "No policy",
"ENABLED_POLICY_SUMMARY": "Do you want to enable policy {{name}}?",
"DISABLED_POLICY_SUMMARY": "Do you want to disable policy {{name}}?",

View File

@ -1636,6 +1636,7 @@
"SCOPE": "Champ d'application",
"SCOPE_SINGLE_PEER": "Pair unique",
"SCOPE_ALL_PEERS": "Tous les pairs",
"EXTRA_ATTRIBUTES": "Attributs supplémentaires",
"NO_POLICY": "Aucune stratégie",
"ENABLED_POLICY_SUMMARY": "Voulez-vous activer la stratégie {{name}} ?",
"DISABLED_POLICY_SUMMARY": "Voulez-vous désactiver la stratégie {{name}} ?",

View File

@ -483,7 +483,6 @@
"CLOUD_EVENT": "클라우드이벤트",
"PAYLOAD_DATA": "페이로드 데이터",
"SLACK_RATE_LIMIT": "Slack 속도 제한에 유의하세요."
},
"GROUP": {
"GROUP": "그룹",
@ -934,7 +933,6 @@
"LDAP_GROUP_MEMBERSHIP_INFO": "속성은 LDAP 그룹의 멤버십을 나타내며 기본값은 memberof이며 일부 LDAP 서버에서는 \"ismemberof\"일 수 있습니다. LDAP 그룹 관련 기능을 활성화해야 하는 경우 이 필드를 비워둘 수 없습니다.",
"GROUP_SCOPE": "LDAP 그룹 검색 범위",
"GROUP_SCOPE_INFO": "그룹을 검색할 범위는 기본적으로 Subtree를 선택합니다."
},
"UAA": {
"ENDPOINT": "UAA 엔드포인트",
@ -1308,7 +1306,6 @@
"ON": "on",
"AT": "at",
"NOSCHEDULE": "예약내역을 가져오던 중 에러가 발생했습니다"
},
"GC": {
"CURRENT_SCHEDULE": "가비지 컬렉션 예약",
@ -1630,6 +1627,7 @@
"SCOPE": "범위",
"SCOPE_SINGLE_PEER": "싱글 피어",
"SCOPE_ALL_PEERS": "모든 피어",
"EXTRA_ATTRIBUTES": "추가 속성",
"NO_POLICY": "정책 없음",
"ENABLED_POLICY_SUMMARY": "정책{{name}}을 활성화하시겠습니까?",
"DISABLED_POLICY_SUMMARY": "정책{{name}}을 비활성화하시겠습니까?",

View File

@ -933,7 +933,6 @@
"LDAP_GROUP_MEMBERSHIP_INFO": "Atributo que informa a lista de grupos do usuário. Se não informado, o nome \"memberof\" será usado. Alguns servidores LDAP utilizam o atributo \"ismemberof\". This field cannot be empty if you need to enable the LDAP group related feature.",
"GROUP_SCOPE": "LDAP Group Search Scope",
"GROUP_SCOPE_INFO": "O escopo que deve ser utilizado na busca por grupos, utiliza Subtree por padrão."
},
"UAA": {
"ENDPOINT": "Endereço UAA",
@ -1303,7 +1302,6 @@
"ON": "em",
"AT": "às",
"NOSCHEDULE": "Ocorreu um erro na rotina Get"
},
"GC": {
"CURRENT_SCHEDULE": "Agenda",
@ -1633,6 +1631,7 @@
"SCOPE": "Escopo",
"SCOPE_SINGLE_PEER": "Par único",
"SCOPE_ALL_PEERS": "Todos os pares",
"EXTRA_ATTRIBUTES": "Atributos extras",
"NO_POLICY": "Nenhuma política",
"ENABLED_POLICY_SUMMARY": "Gostaria de habilitar a política {{name}}?",
"DISABLED_POLICY_SUMMARY": "Gostaria de desabilitar a política {{name}}?",

View File

@ -936,7 +936,6 @@
"LDAP_GROUP_MEMBERSHIP_INFO": "Öznitelik, LDAP grubunun üyeliğini gösterir, varsayılan değer memberof, bazı LDAP sunucularında \"ismemberof\" olabilir. This field cannot be empty if you need to enable the LDAP group related feature.",
"GROUP_SCOPE": "LDAP Group Search Scope",
"GROUP_SCOPE_INFO": "Grupları aramak için kapsamı, Varsayılan olarak Alt Ağaç'ı seçin."
},
"UAA": {
"ENDPOINT": "UAA Uç Noktası",
@ -1310,7 +1309,6 @@
"ON": "on",
"AT": "at",
"NOSCHEDULE": "Takvimlendirme de bir hata oluştu"
},
"GC": {
"CURRENT_SCHEDULE": "Schedule to GC",
@ -1636,6 +1634,7 @@
"SCOPE": "Scope",
"SCOPE_SINGLE_PEER": "Single Peer",
"SCOPE_ALL_PEERS": "All Peers",
"EXTRA_ATTRIBUTES": "Extra Attributes",
"NO_POLICY": "No policy",
"ENABLED_POLICY_SUMMARY": "Do you want to enable policy {{name}}?",
"DISABLED_POLICY_SUMMARY": "Do you want to disable policy {{name}}?",

View File

@ -937,7 +937,6 @@
"GROUP_SCOPE_INFO": "搜索组的范围,默认值为\"子树\"",
"GROUP_ATTACH_PARALLEL": "LDAP组并行同步",
"GROUP_ATTACH_PARALLEL_INFO": "打开这个选项时LDAP组的信息是并行同步到Harbor, 这样可以防止用户组太多时造成的登录超时如果关闭这个选项LDAP组信息是顺序同步到Harbor"
},
"UAA": {
"ENDPOINT": "UAA Endpoint",
@ -1309,7 +1308,6 @@
"ON": " ",
"AT": " ",
"NOSCHEDULE": "获取schedule时出现错误"
},
"GC": {
"CURRENT_SCHEDULE": "当前定时任务",
@ -1635,6 +1633,7 @@
"SCOPE": "范围",
"SCOPE_SINGLE_PEER": "单节点",
"SCOPE_ALL_PEERS": "全节点",
"EXTRA_ATTRIBUTES": "额外属性",
"NO_POLICY": "暂无记录",
"ENABLED_POLICY_SUMMARY": "是否启用策略 {{name}}?",
"DISABLED_POLICY_SUMMARY": "是否禁用策略 {{name}}?",

View File

@ -1631,6 +1631,7 @@
"SCOPE": "範圍",
"SCOPE_SINGLE_PEER": "單節點",
"SCOPE_ALL_PEERS": "全節點",
"EXTRA_ATTRIBUTES": "額外屬性",
"NO_POLICY": "無原則",
"ENABLED_POLICY_SUMMARY": "您是否要啟用原則 {{name}}",
"DISABLED_POLICY_SUMMARY": "您是否要停用原則 {{name}}",

View File

@ -484,6 +484,7 @@ func convertPolicyToPayload(policy *policy.Schema) (*models.PreheatPolicy, error
ProviderID: policy.ProviderID,
Trigger: policy.TriggerStr,
Scope: policy.Scope,
ExtraAttrs: policy.ExtraAttrsStr,
UpdateTime: strfmt.DateTime(policy.UpdatedTime),
}, nil
}
@ -513,6 +514,7 @@ func convertParamPolicyToModelPolicy(model *models.PreheatPolicy) (*policy.Schem
TriggerStr: model.Trigger,
Enabled: model.Enabled,
Scope: model.Scope,
ExtraAttrsStr: model.ExtraAttrs,
CreatedAt: time.Time(model.CreationTime),
UpdatedTime: time.Time(model.UpdateTime),
}, nil