diff --git a/api/replication_policy.go b/api/replication_policy.go index 4f851a343..f3b481946 100644 --- a/api/replication_policy.go +++ b/api/replication_policy.go @@ -1,16 +1,16 @@ /* - Copyright (c) 2016 VMware, Inc. All Rights Reserved. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + Copyright (c) 2016 VMware, Inc. All Rights Reserved. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ package api @@ -121,6 +121,16 @@ func (pa *RepPolicyAPI) Post() { pa.CustomAbort(http.StatusBadRequest, fmt.Sprintf("target %d does not exist", policy.TargetID)) } + policies, err := dao.GetRepPolicyByProjectAndTarget(policy.ProjectID, policy.TargetID) + if err != nil { + log.Errorf("failed to get policy [project ID: %d,targetID: %d]: %v", policy.ProjectID, policy.TargetID, err) + pa.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) + } + + if len(policies) > 0 { + pa.CustomAbort(http.StatusConflict, "policy already exists with the same project and target") + } + pid, err := dao.AddRepPolicy(*policy) if err != nil { log.Errorf("Failed to add policy to DB, error: %v", err) @@ -159,6 +169,7 @@ func (pa *RepPolicyAPI) Put() { policy.ProjectID = originalPolicy.ProjectID pa.Validate(policy) + // check duplicate name if policy.Name != originalPolicy.Name { po, err := dao.GetRepPolicyByName(policy.Name) if err != nil { @@ -172,6 +183,12 @@ func (pa *RepPolicyAPI) Put() { } if policy.TargetID != originalPolicy.TargetID { + //target of policy can not be modified when the policy is enabled + if originalPolicy.Enabled == 1 { + pa.CustomAbort(http.StatusBadRequest, "target of policy can not be modified when the policy is enabled") + } + + // check the existance of target target, err := dao.GetRepTarget(policy.TargetID) if err != nil { log.Errorf("failed to get target %d: %v", policy.TargetID, err) @@ -181,67 +198,90 @@ func (pa *RepPolicyAPI) Put() { if target == nil { pa.CustomAbort(http.StatusBadRequest, fmt.Sprintf("target %d does not exist", policy.TargetID)) } + + // check duplicate policy with the same project and target + policies, err := dao.GetRepPolicyByProjectAndTarget(policy.ProjectID, policy.TargetID) + if err != nil { + log.Errorf("failed to get policy [project ID: %d,targetID: %d]: %v", policy.ProjectID, policy.TargetID, err) + pa.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) + } + + if len(policies) > 0 { + pa.CustomAbort(http.StatusConflict, "policy already exists with the same project and target") + } } policy.ID = id - isTargetChanged := !(policy.TargetID == originalPolicy.TargetID) - isEnablementChanged := !(policy.Enabled == policy.Enabled) + /* + isTargetChanged := !(policy.TargetID == originalPolicy.TargetID) + isEnablementChanged := !(policy.Enabled == policy.Enabled) - var shouldStop, shouldTrigger bool + var shouldStop, shouldTrigger bool - // if target and enablement are not changed, do nothing - if !isTargetChanged && !isEnablementChanged { - shouldStop = false - shouldTrigger = false - } else if !isTargetChanged && isEnablementChanged { - // target is not changed, but enablement is changed - if policy.Enabled == 0 { - shouldStop = true - shouldTrigger = false - } else { - shouldStop = false - shouldTrigger = true - } - } else if isTargetChanged && !isEnablementChanged { - // target is changed, but enablement is not changed - if policy.Enabled == 0 { - // enablement is 0, do nothing + // if target and enablement are not changed, do nothing + if !isTargetChanged && !isEnablementChanged { shouldStop = false shouldTrigger = false + } else if !isTargetChanged && isEnablementChanged { + // target is not changed, but enablement is changed + if policy.Enabled == 0 { + shouldStop = true + shouldTrigger = false + } else { + shouldStop = false + shouldTrigger = true + } + } else if isTargetChanged && !isEnablementChanged { + // target is changed, but enablement is not changed + if policy.Enabled == 0 { + // enablement is 0, do nothing + shouldStop = false + shouldTrigger = false + } else { + // enablement is 1, so stop original target's jobs + // and trigger new target's jobs + shouldStop = true + shouldTrigger = true + } } else { - // enablement is 1, so stop original target's jobs - // and trigger new target's jobs - shouldStop = true - shouldTrigger = true - } - } else { - // both target and enablement are changed + // both target and enablement are changed - // enablement: 1 -> 0 - if policy.Enabled == 0 { - shouldStop = true - shouldTrigger = false - } else { - shouldStop = false - shouldTrigger = true + // enablement: 1 -> 0 + if policy.Enabled == 0 { + shouldStop = true + shouldTrigger = false + } else { + shouldStop = false + shouldTrigger = true + } } - } - if shouldStop { - if err := postReplicationAction(id, "stop"); err != nil { - log.Errorf("failed to stop replication of %d: %v", id, err) + if shouldStop { + if err := postReplicationAction(id, "stop"); err != nil { + log.Errorf("failed to stop replication of %d: %v", id, err) + pa.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) + } + log.Infof("replication of %d has been stopped", id) + } + + if err = dao.UpdateRepPolicy(policy); err != nil { + log.Errorf("failed to update policy %d: %v", id, err) pa.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) } - log.Infof("replication of %d has been stopped", id) - } - if err = dao.UpdateRepPolicy(policy); err != nil { - log.Errorf("failed to update policy %d: %v", id, err) - pa.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) - } + if shouldTrigger { + go func() { + if err := TriggerReplication(id, "", nil, models.RepOpTransfer); err != nil { + log.Errorf("failed to trigger replication of %d: %v", id, err) + } else { + log.Infof("replication of %d triggered", id) + } + }() + } + */ - if shouldTrigger { + if policy.Enabled != originalPolicy.Enabled && policy.Enabled == 1 { go func() { if err := TriggerReplication(id, "", nil, models.RepOpTransfer); err != nil { log.Errorf("failed to trigger replication of %d: %v", id, err) diff --git a/api/target.go b/api/target.go index 089771c2b..40464e46d 100644 --- a/api/target.go +++ b/api/target.go @@ -190,6 +190,16 @@ func (t *TargetAPI) Post() { t.CustomAbort(http.StatusConflict, "name is already used") } + ta, err = dao.GetRepTargetByConnInfo(target.URL, target.Username) + if err != nil { + log.Errorf("failed to get target [ %s %s ]: %v", target.URL, target.Username, err) + t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) + } + + if ta != nil { + t.CustomAbort(http.StatusConflict, "the connection information[ endpoint, username ] is conflict with other target") + } + if len(target.Password) != 0 { target.Password = utils.ReversibleEncrypt(target.Password) } @@ -217,6 +227,24 @@ func (t *TargetAPI) Put() { t.CustomAbort(http.StatusNotFound, http.StatusText(http.StatusNotFound)) } + policies, err := dao.GetRepPolicyByTarget(id) + if err != nil { + log.Errorf("failed to get policies according target %d: %v", id, err) + t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) + } + + hasEnabledPolicy := false + for _, policy := range policies { + if policy.Enabled == 1 { + hasEnabledPolicy = true + break + } + } + + if hasEnabledPolicy { + t.CustomAbort(http.StatusBadRequest, "the target is associated with policy which is enabled") + } + target := &models.RepTarget{} t.DecodeJSONReqAndValidate(target) @@ -232,6 +260,18 @@ func (t *TargetAPI) Put() { } } + if target.URL != originalTarget.URL || target.Username != originalTarget.Username { + ta, err := dao.GetRepTargetByConnInfo(target.URL, target.Username) + if err != nil { + log.Errorf("failed to get target [ %s %s ]: %v", target.URL, target.Username, err) + t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) + } + + if ta != nil { + t.CustomAbort(http.StatusConflict, "the connection information[ endpoint, username ] is conflict with other target") + } + } + target.ID = id if len(target.Password) != 0 { @@ -273,3 +313,27 @@ func (t *TargetAPI) Delete() { t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) } } + +// ListPolicies ... +func (t *TargetAPI) ListPolicies() { + id := t.GetIDFromURL() + + target, err := dao.GetRepTarget(id) + if err != nil { + log.Errorf("failed to get target %d: %v", id, err) + t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) + } + + if target == nil { + t.CustomAbort(http.StatusNotFound, http.StatusText(http.StatusNotFound)) + } + + policies, err := dao.GetRepPolicyByTarget(id) + if err != nil { + log.Errorf("failed to get policies according target %d: %v", id, err) + t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) + } + + t.Data["json"] = policies + t.ServeJSON() +} diff --git a/dao/dao_test.go b/dao/dao_test.go index 1adbce2c0..7742a3dda 100644 --- a/dao/dao_test.go +++ b/dao/dao_test.go @@ -926,6 +926,21 @@ func TestGetRepPolicyByTarget(t *testing.T) { } } +func TestGetRepPolicyByProjectAndTarget(t *testing.T) { + policies, err := GetRepPolicyByProjectAndTarget(1, targetID) + if err != nil { + t.Fatalf("failed to get policy according project %d and target %d: %v", 1, targetID, err) + } + + if len(policies) == 0 { + t.Fatal("unexpected length of policies 0, expected is >0") + } + + if policies[0].ID != policyID { + t.Fatalf("unexpected policy: %d, expected: %d", policies[0].ID, policyID) + } +} + func TestGetRepPolicyByName(t *testing.T) { policy, err := GetRepPolicy(policyID) if err != nil { diff --git a/dao/replication_job.go b/dao/replication_job.go index f1f533b4d..98842db38 100644 --- a/dao/replication_job.go +++ b/dao/replication_job.go @@ -52,6 +52,20 @@ func GetRepTargetByName(name string) (*models.RepTarget, error) { return &t, err } +// GetRepTargetByConnInfo ... +func GetRepTargetByConnInfo(endpoint, username string) (*models.RepTarget, error) { + o := GetOrmer() + t := models.RepTarget{ + URL: endpoint, + Username: username, + } + err := o.Read(&t, "URL", "Username") + if err == orm.ErrNoRows { + return nil, nil + } + return &t, err +} + // DeleteRepTarget ... func DeleteRepTarget(id int64) error { o := GetOrmer() @@ -206,6 +220,20 @@ func GetRepPolicyByTarget(targetID int64) ([]*models.RepPolicy, error) { return policies, nil } +// GetRepPolicyByProjectAndTarget ... +func GetRepPolicyByProjectAndTarget(projectID, targetID int64) ([]*models.RepPolicy, error) { + o := GetOrmer() + sql := `select * from replication_policy where project_id = ? and target_id = ?` + + var policies []*models.RepPolicy + + if _, err := o.Raw(sql, projectID, targetID).QueryRows(&policies); err != nil { + return nil, err + } + + return policies, nil +} + // UpdateRepPolicy ... func UpdateRepPolicy(policy *models.RepPolicy) error { o := GetOrmer() diff --git a/ui/router.go b/ui/router.go index c560b0d07..3917d34b1 100644 --- a/ui/router.go +++ b/ui/router.go @@ -76,6 +76,7 @@ func initRouters() { beego.Router("/api/targets/", &api.TargetAPI{}, "get:List") beego.Router("/api/targets/", &api.TargetAPI{}, "post:Post") beego.Router("/api/targets/:id([0-9]+)", &api.TargetAPI{}) + beego.Router("/api/targets/:id([0-9]+)/policies/", &api.TargetAPI{}, "get:ListPolicies") beego.Router("/api/targets/ping", &api.TargetAPI{}, "post:Ping") beego.Router("/api/users/:id/sysadmin", &api.UserAPI{}, "put:ToggleUserAdminRole") beego.Router("/api/repositories/top", &api.RepositoryAPI{}, "get:GetTopRepos")