diff --git a/agreementbot/agreementworker.go b/agreementbot/agreementworker.go index 2cdcfa511..3e5406dd5 100644 --- a/agreementbot/agreementworker.go +++ b/agreementbot/agreementworker.go @@ -550,7 +550,7 @@ func (b *BaseAgreementWorker) InitiateNewAgreement(cph ConsumerProtocolHandler, // for cluster type and check for namespace compatibility consumerNamespace := "" if nodeType == persistence.DEVICE_TYPE_CLUSTER { - t_comp, consumerNamespace, t_reason = compcheck.CheckClusterNamespaceCompatibility(nodeType, exchangeDev.ClusterNamespace, wi.ConsumerPolicy.ClusterNamespace, topSvcDef.GetClusterDeployment(), false, msgPrinter) + t_comp, consumerNamespace, t_reason = compcheck.CheckClusterNamespaceCompatibility(nodeType, exchangeDev.ClusterNamespace, exchangeDev.IsNamespaceScoped, wi.ConsumerPolicy.ClusterNamespace, topSvcDef.GetClusterDeployment(), wi.ConsumerPolicy.PatternId, false, msgPrinter) if !t_comp { glog.Warningf(BAWlogstring(workerId, fmt.Sprintf("cannot make agreement with node %v for service %v/%v %v. %v", wi.Device.Id, workload.Org, workload.WorkloadURL, workload.Version, t_reason))) return diff --git a/agreementbot/consumer_protocol_handler.go b/agreementbot/consumer_protocol_handler.go index 4754b92a6..b092ab5a6 100644 --- a/agreementbot/consumer_protocol_handler.go +++ b/agreementbot/consumer_protocol_handler.go @@ -319,13 +319,24 @@ func (b *BaseConsumerProtocolHandler) HandlePolicyChanged(cmd *PolicyChangedComm } continue } else if err := b.pm.MatchesMine(cmd.Msg.Org(), pol); err != nil { + if glog.V(5) { + glog.Infof(BCPHlogstring(b.Name(), fmt.Sprintf("cmd msg org matches mine for agreement %v", ag.CurrentAgreementId))) + } agStillValid := false policyMatches := true noNewPriority := false + clusterNSNotChange := true if ag.Pattern == "" { - policyMatches, noNewPriority = b.HandlePolicyChangeForAgreement(ag, cmd.Msg.OldPolicy(), cph) + policyMatches, noNewPriority, clusterNSNotChange = b.HandlePolicyChangeForAgreement(ag, cmd.Msg.OldPolicy(), cph) agStillValid = policyMatches && noNewPriority + if ag.GetDeviceType() == persistence.DEVICE_TYPE_CLUSTER { + agStillValid = agStillValid && clusterNSNotChange + } + } + + if glog.V(5) { + glog.Infof(BCPHlogstring(b.Name(), fmt.Sprintf("for current agreement %v: agStillValid: %v, policyMatches: %v, noNewPriority: %v, clusterNSNotChange: %v", ag.CurrentAgreementId, agStillValid, policyMatches, noNewPriority, clusterNSNotChange))) } if !agStillValid { @@ -373,8 +384,9 @@ func (b *BaseConsumerProtocolHandler) HandlePolicyChanged(cmd *PolicyChangedComm // first bool is true if the policy still matches, false otherwise // second bool is true unless a higher priority workload than the current one has been added or changed +// third bool is true if the cluster namespace is not changed, this return value should be check only when device type is cluster // if an error occurs, both will be false -func (b *BaseConsumerProtocolHandler) HandlePolicyChangeForAgreement(ag persistence.Agreement, oldPolicy *policy.Policy, cph ConsumerProtocolHandler) (bool, bool) { +func (b *BaseConsumerProtocolHandler) HandlePolicyChangeForAgreement(ag persistence.Agreement, oldPolicy *policy.Policy, cph ConsumerProtocolHandler) (bool, bool, bool) { if glog.V(5) { glog.Infof(BCPHlogstring(b.Name(), fmt.Sprintf("attempting to update agreement %v due to change in policy", ag.CurrentAgreementId))) } @@ -384,7 +396,7 @@ func (b *BaseConsumerProtocolHandler) HandlePolicyChangeForAgreement(ag persiste for _, svcId := range ag.ServiceId { if svcPol, err := exchange.GetServicePolicyWithId(b, svcId); err != nil { glog.Errorf(BCPHlogstring(b.Name(), fmt.Sprintf("failed to get service policy for %v from the exchange: %v", svcId, err))) - return false, false + return false, false, false } else if svcPol != nil { svcAllPol.MergeWith(&svcPol.ExternalPolicy, false) } @@ -396,23 +408,23 @@ func (b *BaseConsumerProtocolHandler) HandlePolicyChangeForAgreement(ag persiste _, busPol, err := compcheck.GetBusinessPolicy(busPolHandler, ag.PolicyName, true, msgPrinter) if err != nil { glog.Errorf(BCPHlogstring(b.Name(), fmt.Sprintf("failed to get business policy %v/%v from the exchange: %v", ag.Org, ag.PolicyName, err))) - return false, false + return false, false, false } nodePolHandler := exchange.GetHTTPNodePolicyHandler(b) _, nodePol, err := compcheck.GetNodePolicy(nodePolHandler, ag.DeviceId, msgPrinter) if err != nil { glog.Errorf(BCPHlogstring(b.Name(), fmt.Sprintf("failed to get node policy for %v from the exchange.", ag.DeviceId))) - return false, false + return false, false, false } dev, err := exchange.GetExchangeDevice(b.GetHTTPFactory(), ag.DeviceId, b.GetExchangeId(), b.GetExchangeToken(), b.GetExchangeURL()) if err != nil { glog.Errorf(BCPHlogstring(b.Name(), fmt.Sprintf("failed to get node %v from the exchange.", ag.DeviceId))) - return false, false + return false, false, false } else if dev == nil { glog.Errorf(BCPHlogstring(b.Name(), fmt.Sprintf("Device %v does not exist in the exchange.", ag.DeviceId))) - return false, false + return false, false, false } nodeArch := dev.Arch @@ -428,30 +440,31 @@ func (b *BaseConsumerProtocolHandler) HandlePolicyChangeForAgreement(ag persiste // skip for now if not all built-in properties are in the node policy // this will get called again after the node updates its policy with the built-ins if !externalpolicy.ContainsAllBuiltInNodeProps(&nodePol.Properties, swVers, dev.GetNodeType()) { - return true, true + return true, true, true } match, reason, producerPol, consumerPol, err := compcheck.CheckPolicyCompatiblility(nodePol, busPol, &svcAllPol, nodeArch, nil) if !match { glog.V(5).Infof(BCPHlogstring(b.Name(), fmt.Sprintf("agreement %v is not longer in policy. Reason is: %v", ag.CurrentAgreementId, reason))) - return false, true + return false, true, false } // don't send an update if the agreement is not finalized yet if ag.AgreementFinalizedTime == 0 { - return true, true + return true, true, true } // for every priority (in order highest to lowest) in the new policy with priority lower than the current wl // if it's not in the old policy, cancel choice := -1 + nextPriority := policy.GetNextWorkloadChoice(busPol.Workloads, choice) wl := nextPriority wlUsage, err := b.db.FindSingleWorkloadUsageByDeviceAndPolicyName(ag.DeviceId, ag.PolicyName) if err != nil { - return false, false + return false, false, false } // wlUsage is nil if no prioriy is set in the previous policy wlUsagePriority := 0 @@ -462,7 +475,7 @@ func (b *BaseConsumerProtocolHandler) HandlePolicyChangeForAgreement(ag persiste if currentWL := policy.GetWorkloadWithPriority(busPol.Workloads, wlUsagePriority); currentWL == nil { // the current workload priority is no longer in the deployment policy glog.Infof(BCPHlogstring(b.Name(), fmt.Sprintf("current workload priority %v is no longer in policy for agreement %v", wlUsagePriority, ag.CurrentAgreementId))) - return true, false + return true, false, false } else { wl = currentWL } @@ -473,10 +486,22 @@ func (b *BaseConsumerProtocolHandler) HandlePolicyChangeForAgreement(ag persiste matchingWL := policy.GetWorkloadWithPriority(oldPolicy.Workloads, choice) if matchingWL == nil || !matchingWL.IsSame(*nextPriority) { glog.Infof(BCPHlogstring(b.Name(), fmt.Sprintf("Higher priority version added or modified. Cancelling agreement %v", ag.CurrentAgreementId))) - return true, false + return true, false, false } nextPriority = policy.GetNextWorkloadChoice(busPol.Workloads, choice) } + + // check if cluster namespace is changed in new policy + if dev.NodeType == persistence.DEVICE_TYPE_CLUSTER && busPol.ClusterNamespace != oldPolicy.ClusterNamespace { + glog.V(5).Infof(BCPHlogstring(b.Name(), fmt.Sprintf("cluster namespace is changed from %v to %v in busiess policy for agreement %v, checking cluster namespace compatibility ...", oldPolicy.ClusterNamespace, busPol.ClusterNamespace, ag.CurrentAgreementId))) + t_comp, consumerNamespace, t_reason := compcheck.CheckClusterNamespaceCompatibility(dev.NodeType, dev.ClusterNamespace, dev.IsNamespaceScoped, busPol.ClusterNamespace, wl.ClusterDeployment, ag.Pattern, false, msgPrinter) + if !t_comp { + glog.V(5).Infof(BCPHlogstring(b.Name(), fmt.Sprintf("cluster namespace %v is not longer compatible for agreement %v. Reason is: %v", consumerNamespace, ag.CurrentAgreementId, t_reason))) + + } + // new cluster namespace is still compatible + return true, true, false + } } if wl.Arch == "" || wl.Arch == "*" { @@ -486,10 +511,10 @@ func (b *BaseConsumerProtocolHandler) HandlePolicyChangeForAgreement(ag persiste // populate the workload with the deployment string if svcDef, _, err := exchange.GetHTTPServiceHandler(b)(wl.WorkloadURL, wl.Org, wl.Version, wl.Arch); err != nil { glog.Errorf(BCPHlogstring(b.Name(), fmt.Sprintf("error getting service '%v' from the exchange, error: %v", wl, err))) - return false, false + return false, false, false } else if svcDef == nil { glog.Errorf(BCPHlogstring(b.Name(), fmt.Sprintf("Service %v not found in the exchange.", wl))) - return false, false + return false, false, false } else { if dev.NodeType == persistence.DEVICE_TYPE_CLUSTER { wl.ClusterDeployment = svcDef.GetClusterDeploymentString() @@ -503,14 +528,14 @@ func (b *BaseConsumerProtocolHandler) HandlePolicyChangeForAgreement(ag persiste newTsCs, err := policy.Create_Terms_And_Conditions(producerPol, consumerPol, wl, ag.CurrentAgreementId, b.config.AgreementBot.DefaultWorkloadPW, b.config.AgreementBot.NoDataIntervalS, basicprotocol.PROTOCOL_CURRENT_VERSION) if err != nil { glog.Errorf(BCPHlogstring(b.Name(), fmt.Sprintf("error creating new terms and conditions: %v", err))) - return false, false + return false, false, false } ag.LastPolicyUpdateTime = uint64(time.Now().Unix()) b.UpdateAgreement(&ag, basicprotocol.MsgUpdateTypePolicyChange, newTsCs, cph) - return true, true + return true, true, true } func (b *BaseConsumerProtocolHandler) HandlePolicyDeleted(cmd *PolicyDeletedCommand, cph ConsumerProtocolHandler) { @@ -581,7 +606,7 @@ func (b *BaseConsumerProtocolHandler) HandleServicePolicyChanged(cmd *ServicePol if agreements, err := b.db.FindAgreements([]persistence.AFilter{persistence.UnarchivedAFilter(), InProgress()}, cph.Name()); err == nil { for _, ag := range agreements { if ag.Pattern == "" && ag.PolicyName == fmt.Sprintf("%v/%v", cmd.Msg.BusinessPolOrg, cmd.Msg.BusinessPolName) && ag.ServiceId[0] == cmd.Msg.ServiceId { - policyMatches, noNewPriority := b.HandlePolicyChangeForAgreement(ag, nil, cph) + policyMatches, noNewPriority, _ := b.HandlePolicyChangeForAgreement(ag, nil, cph) agStillValid := policyMatches && noNewPriority if !agStillValid { glog.Warningf(BCPHlogstring(b.Name(), fmt.Sprintf("agreement %v has a service policy %v that has changed.", ag.CurrentAgreementId, ag.ServiceId))) @@ -606,7 +631,7 @@ func (b *BaseConsumerProtocolHandler) HandleNodePolicyChanged(cmd *NodePolicyCha if agreements, err := b.db.FindAgreements([]persistence.AFilter{persistence.UnarchivedAFilter(), InProgress()}, cph.Name()); err == nil { for _, ag := range agreements { if ag.Pattern == "" && ag.DeviceId == cutil.FormOrgSpecUrl(cmd.Msg.NodeId, cmd.Msg.NodePolOrg) { - policyMatches, noNewPriority := b.HandlePolicyChangeForAgreement(ag, nil, cph) + policyMatches, noNewPriority, _ := b.HandlePolicyChangeForAgreement(ag, nil, cph) agStillValid := policyMatches && noNewPriority if !agStillValid { glog.Warningf(BCPHlogstring(b.Name(), fmt.Sprintf("agreement %v has a node policy %v that has changed.", ag.CurrentAgreementId, ag.ServiceId))) diff --git a/api/input.go b/api/input.go index a442d8179..33a69b1ab 100644 --- a/api/input.go +++ b/api/input.go @@ -30,6 +30,7 @@ type HorizonDevice struct { Name *string `json:"name,omitempty"` NodeType *string `json:"nodeType,omitempty"` ClusterNamespace *string `json:"clusterNamespace"` + NamespaceScoped *bool `json:"NamespaceScoped,omitempty"` Token *string `json:"token,omitempty"` TokenLastValidTime *uint64 `json:"token_last_valid_time,omitempty"` TokenValid *bool `json:"token_valid,omitempty"` @@ -69,6 +70,11 @@ func (h HorizonDevice) String() string { clusterNs = *h.ClusterNamespace } + isNS := false + if h.NamespaceScoped != nil { + isNS = *h.NamespaceScoped + } + ha_group := "" if h.HAGroup != nil { ha_group = *h.HAGroup @@ -89,7 +95,7 @@ func (h HorizonDevice) String() string { tv = *h.TokenValid } - return fmt.Sprintf("Id: %v, Org: %v, Pattern: %v, Name: %v, NodeType: %v, ClusterNamespace: %v, HAGroup: %v, Token: [%v], TokenLastValidTime: %v, TokenValid: %v, %v", id, org, pat, name, nodeType, clusterNs, ha_group, cred, tlvt, tv, h.Config) + return fmt.Sprintf("Id: %v, Org: %v, Pattern: %v, Name: %v, NodeType: %v, ClusterNamespace: %v, NamespaceScoped: %v, HAGroup: %v, Token: [%v], TokenLastValidTime: %v, TokenValid: %v, %v", id, org, pat, name, nodeType, clusterNs, isNS, ha_group, cred, tlvt, tv, h.Config) } // This is a type conversion function but note that the token field within the persistent @@ -121,6 +127,9 @@ func ConvertFromPersistentHorizonDevice(pDevice *persistence.ExchangeDevice) *Ho if pDevice.NodeType == persistence.DEVICE_TYPE_CLUSTER { ns := cutil.GetClusterNamespace() hDevice.ClusterNamespace = &ns + + isNS := cutil.IsNamespaceScoped() + hDevice.NamespaceScoped = &isNS } return &hDevice diff --git a/api/path_node.go b/api/path_node.go index 9c3de53ec..9d9847ea5 100644 --- a/api/path_node.go +++ b/api/path_node.go @@ -251,6 +251,14 @@ func CreateHorizonDevice(device *HorizonDevice, if err := patchDeviceHandler(deviceId, *device.Token, &pdr); err != nil { return errorhandler(NewSystemError(fmt.Sprintf("error updating cluster namespace for the exchange node. %v", err))), nil, nil } + + pdr = exchange.PatchDeviceRequest{} + isNS := cutil.IsNamespaceScoped() + pdr.IsNamespaceScoped = &isNS + if err := patchDeviceHandler(deviceId, *device.Token, &pdr); err != nil { + return errorhandler(NewSystemError(fmt.Sprintf("error updating cluster agent scope for the exchange node. %v", err))), nil, nil + } + } // Return 2 device objects, the first is the fully populated newly created device object. The second is a device diff --git a/api/path_node_configstate.go b/api/path_node_configstate.go index 694ec5dd7..1d6dfa64b 100644 --- a/api/path_node_configstate.go +++ b/api/path_node_configstate.go @@ -16,7 +16,6 @@ import ( "github.com/open-horizon/anax/persistence" "github.com/open-horizon/anax/policy" "github.com/open-horizon/anax/semanticversion" - "os" "strings" ) @@ -358,15 +357,19 @@ func getSpecRefsForPattern(nodeType string, patName string, glog.V(5).Infof(apiLogString(fmt.Sprintf("working with pattern definition %v", patternDef))) - nodeNamespace := os.Getenv("AGENT_NAMESPACE") + // Uncomment this section after exchange add "isNamespaceScoped" field + nodeNamespace := cutil.GetClusterNamespace() + isNamespaceScoped := cutil.IsNamespaceScoped() + if nodeType == persistence.DEVICE_TYPE_CLUSTER { if nodeNamespace == "" { + // TODO: a better way to detect cluster agent namespace nodeNamespace = externalpolicy.DEFAULT_NODE_K8S_NAMESPACE } - if nodeNamespace != externalpolicy.DEFAULT_NODE_K8S_NAMESPACE { - if patternDef.ClusterNamespace != "" && patternDef.ClusterNamespace != nodeNamespace { - return nil, nil, NewSystemError(fmt.Sprintf("Pattern cluster namespace is different from agent namespace. Cluster namespace in pattern is %v, agent namespace is %v", patternDef.ClusterNamespace, nodeNamespace)) - } + + glog.V(5).Infof(apiLogString(fmt.Sprintf("checking cluster namespace comptibility in pattern %v", patId))) + if err := compcheck.ValidatePatternClusterNamespace(isNamespaceScoped, nodeNamespace, patternDef.ClusterNamespace, patId, nil); err != nil { + return nil, nil, NewSystemError(err.Error()) } } @@ -416,7 +419,7 @@ func getSpecRefsForPattern(nodeType string, patName string, if nodeType == persistence.DEVICE_TYPE_CLUSTER { // Ignore service that has namespace conflict - if compatible, _, reason := compcheck.CheckClusterNamespaceCompatibility(nodeType, nodeNamespace, patternDef.ClusterNamespace, serviceDef.ClusterDeployment, true, nil); !compatible { + if compatible, _, reason := compcheck.CheckClusterNamespaceCompatibility(nodeType, cutil.GetClusterNamespace(), cutil.IsNamespaceScoped(), patternDef.ClusterNamespace, serviceDef.ClusterDeployment, patId, true, nil); !compatible { // warning glog.Infof(apiLogString(fmt.Sprintf("skipping service %v/%v because %v", service.ServiceOrg, service.ServiceURL, reason))) continue diff --git a/cli/deploycheck/allcomp.go b/cli/deploycheck/allcomp.go index 3118a168c..8fa18ef3c 100644 --- a/cli/deploycheck/allcomp.go +++ b/cli/deploycheck/allcomp.go @@ -21,7 +21,7 @@ import ( ) // check if the policies are compatible -func AllCompatible(org string, userPw string, nodeIds []string, haGroupName string, nodeArch string, nodeType string, nodeNamespace string, nodeOrg string, +func AllCompatible(org string, userPw string, nodeIds []string, haGroupName string, nodeArch string, nodeType string, nodeNamespace string, nodeIsNamespaceScoped bool, nodeOrg string, nodePolFile string, nodeUIFile string, businessPolId string, businessPolFile string, patternId string, patternFile string, servicePolFile string, svcDefFiles []string, checkAllSvcs bool, showDetail bool) { @@ -59,6 +59,7 @@ func AllCompatible(org string, userPw string, nodeIds []string, haGroupName stri compCheckInput.NodeArch = nodeArch compCheckInput.NodeType = nodeType compCheckInput.NodeClusterNS = nodeNamespace + compCheckInput.NodeNamespaceScoped = nodeIsNamespaceScoped compCheckInput.NodeOrg = nodeOrg compCheckInput.BusinessPolicy = bp compCheckInput.PatternId = patternId @@ -119,7 +120,7 @@ func AllCompatible(org string, userPw string, nodeIds []string, haGroupName stri if bUseLocalNodeForPolicy || bUseLocalNodeForUI { // get id from local node, check arch - compCheckInput.NodeId, compCheckInput.NodeArch, compCheckInput.NodeType, compCheckInput.NodeClusterNS, compCheckInput.NodeOrg = getLocalNodeInfo(nodeArch, nodeType, nodeNamespace, nodeOrg) + compCheckInput.NodeId, compCheckInput.NodeArch, compCheckInput.NodeType, compCheckInput.NodeClusterNS, compCheckInput.NodeNamespaceScoped, compCheckInput.NodeOrg = getLocalNodeInfo(nodeArch, nodeType, nodeNamespace, nodeIsNamespaceScoped, nodeOrg) } if nodeType == "" && compCheckInput.NodeId != "" { @@ -325,7 +326,7 @@ func verifyCompCheckParameters(org string, userPw string, } // get node info and check node arch and org against the input arch -func getLocalNodeInfo(inputArch string, inputType string, inputNamespace string, inputOrg string) (string, string, string, string, string) { +func getLocalNodeInfo(inputArch string, inputType string, inputNamespace string, inputIsNamespaceScoped bool, inputOrg string) (string, string, string, string, bool, string) { // get message printer msgPrinter := i18n.GetMessagePrinter() @@ -334,6 +335,7 @@ func getLocalNodeInfo(inputArch string, inputType string, inputNamespace string, nodeOrg := "" arch := cutil.ArchString() namespace := "" + isNamespaceScoped := false horDevice := api.HorizonDevice{} cliutils.HorizonGet("node", []int{200}, &horDevice, false) @@ -359,9 +361,14 @@ func getLocalNodeInfo(inputArch string, inputType string, inputNamespace string, } if nodeType == persistence.DEVICE_TYPE_CLUSTER && namespace == "" { + // should inspect the namespace by k8s library? namespace = externalpolicy.DEFAULT_NODE_K8S_NAMESPACE } + if horDevice.NamespaceScoped != nil { + isNamespaceScoped = *horDevice.NamespaceScoped + } + // check node architecture if inputArch != "" && inputArch != arch { cliutils.Fatal(cliutils.CLI_INPUT_ERROR, msgPrinter.Sprintf("The node architecture %v specified by -a does not match the architecture of the local node %v.", inputArch, arch)) @@ -377,12 +384,17 @@ func getLocalNodeInfo(inputArch string, inputType string, inputNamespace string, cliutils.Fatal(cliutils.CLI_INPUT_ERROR, msgPrinter.Sprintf("The node cluster namespace %v specified by -s does not match the cluster namespace of the local node %v.", inputNamespace, namespace)) } + // check cluster agent scope + if inputIsNamespaceScoped != isNamespaceScoped { + cliutils.Fatal(cliutils.CLI_INPUT_ERROR, msgPrinter.Sprintf("The node is-namespace-scoped %v specified by --is-namespace-scoped does not match the agent scope of local node %v.", inputIsNamespaceScoped, isNamespaceScoped)) + } + // check node organization if inputOrg != "" && nodeOrg != "" && inputOrg != nodeOrg { cliutils.Fatal(cliutils.CLI_INPUT_ERROR, msgPrinter.Sprintf("The node organization %v specified by -O does not match the organization of the local node %v.", inputType, nodeOrg)) } - return id, arch, nodeType, namespace, nodeOrg + return id, arch, nodeType, namespace, isNamespaceScoped, nodeOrg } // get business policy from exchange or from file. diff --git a/cli/deploycheck/policy.go b/cli/deploycheck/policy.go index ff27a4c45..d569b9e89 100644 --- a/cli/deploycheck/policy.go +++ b/cli/deploycheck/policy.go @@ -31,7 +31,7 @@ func readServicePolicyFile(filePath string, inputFileStruct *exchangecommon.Serv } // check if the policies are compatible -func PolicyCompatible(org string, userPw string, nodeIds []string, haGroupName string, nodeArch string, nodeType string, nodeNamespace string, nodePolFile string, businessPolId string, businessPolFile string, servicePolFile string, svcDefFiles []string, checkAllSvcs bool, showDetail bool) { +func PolicyCompatible(org string, userPw string, nodeIds []string, haGroupName string, nodeArch string, nodeType string, nodeNamespace string, nodeIsNamespaceScoped bool, nodePolFile string, businessPolId string, businessPolFile string, servicePolFile string, svcDefFiles []string, checkAllSvcs bool, showDetail bool) { msgPrinter := i18n.GetMessagePrinter() @@ -73,6 +73,7 @@ func PolicyCompatible(org string, userPw string, nodeIds []string, haGroupName s policyCheckInput.NodeArch = nodeArch policyCheckInput.NodeType = nodeType policyCheckInput.NodeClusterNS = nodeNamespace + policyCheckInput.NodeNamespaceScoped = nodeIsNamespaceScoped policyCheckInput.BusinessPolicy = bp // formalize node id or get node policy @@ -94,7 +95,7 @@ func PolicyCompatible(org string, userPw string, nodeIds []string, haGroupName s if bUseLocalNode { // get id from local node, check arch - policyCheckInput.NodeId, policyCheckInput.NodeArch, policyCheckInput.NodeType, policyCheckInput.NodeClusterNS, _ = getLocalNodeInfo(nodeArch, nodeType, nodeNamespace, "") + policyCheckInput.NodeId, policyCheckInput.NodeArch, policyCheckInput.NodeType, policyCheckInput.NodeClusterNS, policyCheckInput.NodeNamespaceScoped, _ = getLocalNodeInfo(nodeArch, nodeType, nodeNamespace, nodeIsNamespaceScoped, "") // get node policy from local node var np exchangecommon.NodePolicy diff --git a/cli/deploycheck/secretbinding.go b/cli/deploycheck/secretbinding.go index 365c3b85b..676d8c9a2 100644 --- a/cli/deploycheck/secretbinding.go +++ b/cli/deploycheck/secretbinding.go @@ -44,7 +44,7 @@ func SecretBindingCompatible(org string, userPw string, nodeId string, nodeArch msgPrinter.Println() // get id from local node, check arch - sbCheckInput.NodeId, sbCheckInput.NodeArch, sbCheckInput.NodeType, _, sbCheckInput.NodeOrg = getLocalNodeInfo(nodeArch, nodeType, "", nodeOrg) + sbCheckInput.NodeId, sbCheckInput.NodeArch, sbCheckInput.NodeType, _, _, sbCheckInput.NodeOrg = getLocalNodeInfo(nodeArch, nodeType, "", false, nodeOrg) } // put the given service defs into the sbCheckInput diff --git a/cli/deploycheck/userinput.go b/cli/deploycheck/userinput.go index 65ffcb62c..bfe597ac5 100644 --- a/cli/deploycheck/userinput.go +++ b/cli/deploycheck/userinput.go @@ -74,7 +74,7 @@ func UserInputCompatible(org string, userPw string, nodeId string, nodeArch stri if bUseLocalNode { // get id from local node, check arch - uiCheckInput.NodeId, uiCheckInput.NodeArch, uiCheckInput.NodeType, _, _ = getLocalNodeInfo(nodeArch, nodeType, "", "") + uiCheckInput.NodeId, uiCheckInput.NodeArch, uiCheckInput.NodeType, _, _, _ = getLocalNodeInfo(nodeArch, nodeType, "", false, "") // get node user input from local node var node_ui []policy.UserInput diff --git a/cli/exchange/business.go b/cli/exchange/business.go index 3c13ba405..fbfded404 100644 --- a/cli/exchange/business.go +++ b/cli/exchange/business.go @@ -17,6 +17,7 @@ import ( "golang.org/x/text/message" "net/http" "runtime" + "strings" ) // BusinessListPolicy lists all the policies in the org or only the specified policy if one is given @@ -95,7 +96,7 @@ func BusinessAddPolicy(org string, credToUse string, policy string, jsonFilePath ec := cliutils.GetUserExchangeContext(org, credToUse) verifySecretBindingForPolicy(&policyFile, polOrg, ec) - ValidateServiceClusterNS(&policyFile.Service, ec) + ValidateClusterNS(&policyFile, ec) // if the --no-constraints flag is not specified and the given policy has no constraints, alert the user. if (!noConstraints) && policyFile.HasNoConstraints() { @@ -415,6 +416,51 @@ func ValidateSecretBindingForSvcAndDep(secretBinding []exchangecommon.SecretBind return ret, nil } +func ValidateClusterNS(policy *businesspolicy.BusinessPolicy, ec exchange.ExchangeContext) { + ValidateClusterNSWithConstraint(policy, ec) + ValidateServiceClusterNS(&policy.Service, ec) +} + +// Validate cluster namespace specified in the deployment policy has no conflict with constraint, print warning message if conflict is detected +func ValidateClusterNSWithConstraint(policy *businesspolicy.BusinessPolicy, ec exchange.ExchangeContext) { + // get message printer + msgPrinter := i18n.GetMessagePrinter() + + if policy == nil || &policy.Service == nil { + return + } + + if policy.Service.ClusterNamespace != "" && !policy.HasNoConstraints() { + clusterNSInPolicy := policy.Service.ClusterNamespace + + propList := new(externalpolicy.PropertyList) + propList.Add_Property(externalpolicy.Property_Factory(externalpolicy.PROP_NODE_K8S_NAMESPACE, clusterNSInPolicy), false) + + newconstr := externalpolicy.Constraint_Factory() + constrains := policy.Constraints.GetStrings() + for _, constrain := range constrains { + if strings.Contains(constrain, externalpolicy.PROP_NODE_K8S_NAMESPACE) { + // We only need to validate constraint that has "openhorizon.kubernetesNamespace" + newconstr.Add_Constraint(constrain) + } else { + continue + } + } + + // all the "openhorizon.kubernetesNamespace" related constrain (along with other if in the same line) are added + if parsedConstrain, err := externalpolicy.GetParseConstraintWithName(newconstr, externalpolicy.PROP_NODE_K8S_NAMESPACE); err != nil { + cliutils.Fatal(cliutils.CLI_GENERAL_ERROR, msgPrinter.Sprintf("Failed to get constraint with name %v, error: %v", externalpolicy.PROP_NODE_K8S_NAMESPACE, err)) + } else { + requiredProperties := externalpolicy.ConvertParsedConstraintToRequiredProperty(parsedConstrain) + + if err = requiredProperties.IsSatisfiedBy(*propList); err != nil { + msgPrinter.Printf("Warning: kubernetesNamespace defined in the constrain %v is different from the clusterNamespace '%v' specified in the deployment policy, the policy might result in no service deployments: %v", newconstr, clusterNSInPolicy, err) + msgPrinter.Println() + } + } + } +} + // Verify the clusterNamespace is not set for device service. func ValidateServiceClusterNS(serviceRef *businesspolicy.ServiceRef, ec exchange.ExchangeContext) { // get message printer diff --git a/cli/hzn.go b/cli/hzn.go index b32b674f3..88dd90c84 100644 --- a/cli/hzn.go +++ b/cli/hzn.go @@ -190,6 +190,7 @@ Environment Variables: allCompNodeArch := allCompCmd.Flag("arch", msgPrinter.Sprintf("The architecture of the node. It is required when -n is not specified. If omitted, the service of all the architectures referenced in the deployment policy or pattern will be checked for compatibility.")).Short('a').String() allCompNodeType := allCompCmd.Flag("node-type", msgPrinter.Sprintf("The node type. The valid values are 'device' and 'cluster'. The default value is the type of the node provided by -n or current registered device, if omitted.")).Short('t').String() allCompNodeNs := allCompCmd.Flag("cluster-namespace", msgPrinter.Sprintf("The Kubernetes cluster namespace for the node if the node type is 'cluster'. The default value is 'openhorizon-agent', if omitted. The value is ignored when the node type is 'device'")).Short('s').String() + allCompNodIsNamespaceScoped := allCompCmd.Flag("is-namespace-scoped", msgPrinter.Sprintf("The cluster scope for the node is namespace scoped if the node type is 'cluster'. The default value is false, if omitted. The value is ignored when the node type is 'device'")).Bool() allCompNodeOrg := allCompCmd.Flag("node-org", msgPrinter.Sprintf("The organization of the node. The default value is the organization of the node provided by -n or current registered device, if omitted.")).Short('O').String() allCompNodeId := allCompCmd.Flag("node-id", msgPrinter.Sprintf("The Horizon exchange node ID. Mutually exclusive with --ha-group, --node-pol and --node-ui. If omitted, the node ID that the current device is registered with will be used. This flag can be repeated to specify more than one nodes. If you don't prepend a node id with the organization id, it will automatically be prepended with the -o value.")).Short('n').Strings() allCompHAGroup := allCompCmd.Flag("ha-group", msgPrinter.Sprintf("The name of an HA group. The deployment check will be performed on all the nodes within the given HA group. Mutually exclusive with -n, --node-pol and --node-ui.")).String() @@ -207,6 +208,7 @@ Environment Variables: policyCompNodeArch := policyCompCmd.Flag("arch", msgPrinter.Sprintf("The architecture of the node. It is required when -n is not specified. If omitted, the service of all the architectures referenced in the deployment policy will be checked for compatibility.")).Short('a').String() policyCompNodeType := policyCompCmd.Flag("node-type", msgPrinter.Sprintf("The node type. The valid values are 'device' and 'cluster'. The default value is the type of the node provided by -n or current registered device, if omitted.")).Short('t').String() policyCompNodeNs := policyCompCmd.Flag("cluster-namespace", msgPrinter.Sprintf("The Kubernetes cluster namespace for the node if the node type is 'cluster'. The default value is 'openhorizon-agent', if omitted. The value is ignored when the node type is 'device'")).Short('s').String() + policyCompNodeIsNamespaceScoped := policyCompCmd.Flag("is-namespace-scoped", msgPrinter.Sprintf("The cluster scope for the node is namespace scoped if the node type is 'cluster'. The default value is false, if omitted. The value is ignored when the node type is 'device'")).Bool() policyCompNodeId := policyCompCmd.Flag("node-id", msgPrinter.Sprintf("The Horizon exchange node ID. Mutually exclusive with --ha-group and --node-pol. If omitted, the node ID that the current device is registered with will be used. This flag can be repeated to specify more than one nodes. If you don't prepend a node id with the organization id, it will automatically be prepended with the -o value.")).Short('n').Strings() policyCompHAGroup := policyCompCmd.Flag("ha-group", msgPrinter.Sprintf("The name of an HA group. The deployment check will be performed on all the nodes within the given HA group. Mutually exclusive with -n and --node-pol.")).String() policyCompNodePolFile := policyCompCmd.Flag("node-pol", msgPrinter.Sprintf("The JSON input file name containing the node policy. Mutually exclusive with -n, --ha-group.")).String() @@ -1353,13 +1355,13 @@ Environment Variables: case policyRemoveCmd.FullCommand(): policy.Remove(*policyRemoveForce) case policyCompCmd.FullCommand(): - deploycheck.PolicyCompatible(*deploycheckOrg, *deploycheckUserPw, *policyCompNodeId, *policyCompHAGroup, *policyCompNodeArch, *policyCompNodeType, *policyCompNodeNs, *policyCompNodePolFile, *policyCompBPolId, *policyCompBPolFile, *policyCompSPolFile, *policyCompSvcFile, *deploycheckCheckAll, *deploycheckLong) + deploycheck.PolicyCompatible(*deploycheckOrg, *deploycheckUserPw, *policyCompNodeId, *policyCompHAGroup, *policyCompNodeArch, *policyCompNodeType, *policyCompNodeNs, *policyCompNodeIsNamespaceScoped, *policyCompNodePolFile, *policyCompBPolId, *policyCompBPolFile, *policyCompSPolFile, *policyCompSvcFile, *deploycheckCheckAll, *deploycheckLong) case userinputCompCmd.FullCommand(): deploycheck.UserInputCompatible(*deploycheckOrg, *deploycheckUserPw, *userinputCompNodeId, *userinputCompNodeArch, *userinputCompNodeType, *userinputCompNodeUIFile, *userinputCompBPolId, *userinputCompBPolFile, *userinputCompPatternId, *userinputCompPatternFile, *userinputCompSvcFile, *deploycheckCheckAll, *deploycheckLong) case secretCompCmd.FullCommand(): deploycheck.SecretBindingCompatible(*deploycheckOrg, *deploycheckUserPw, *secretCompNodeId, *secretCompNodeArch, *secretCompNodeType, *secretCompNodeOrg, *secretCompDepPolId, *secretCompDepPolFile, *secretCompPatternId, *secretCompPatternFile, *secretCompSvcFile, *deploycheckCheckAll, *deploycheckLong) case allCompCmd.FullCommand(): - deploycheck.AllCompatible(*deploycheckOrg, *deploycheckUserPw, *allCompNodeId, *allCompHAGroup, *allCompNodeArch, *allCompNodeType, *allCompNodeNs, *allCompNodeOrg, *allCompNodePolFile, *allCompNodeUIFile, *allCompBPolId, *allCompBPolFile, *allCompPatternId, *allCompPatternFile, *allCompSPolFile, *allCompSvcFile, *deploycheckCheckAll, *deploycheckLong) + deploycheck.AllCompatible(*deploycheckOrg, *deploycheckUserPw, *allCompNodeId, *allCompHAGroup, *allCompNodeArch, *allCompNodeType, *allCompNodeNs, *allCompNodIsNamespaceScoped, *allCompNodeOrg, *allCompNodePolFile, *allCompNodeUIFile, *allCompBPolId, *allCompBPolFile, *allCompPatternId, *allCompPatternFile, *allCompSPolFile, *allCompSvcFile, *deploycheckCheckAll, *deploycheckLong) case agreementListCmd.FullCommand(): agreement.List(*listArchivedAgreements, *listAgreementId) case agreementCancelCmd.FullCommand(): diff --git a/cli/register/servicewait.go b/cli/register/servicewait.go index 8788c38c1..450abafc0 100644 --- a/cli/register/servicewait.go +++ b/cli/register/servicewait.go @@ -326,7 +326,7 @@ func WaitForService(org string, waitService string, waitTimeout int, pattern str msgPrinter.Println() msgPrinter.Printf("Command output:") msgPrinter.Println() - deploycheck.AllCompatible(userOrg, userPw, []string{}, "", nodeArch, nodeType, "", nodeOrg, "", "", + deploycheck.AllCompatible(userOrg, userPw, []string{}, "", nodeArch, nodeType, "", false, nodeOrg, "", "", "", "", pattern, "", "", []string{}, false, false) } else { msgPrinter.Printf("Using the 'hzn deploycheck userinput -p' command to verify that node, service configuration and pattern are compatible.") diff --git a/compcheck/comp_check.go b/compcheck/comp_check.go index 52a5bc258..c6f7ea9d2 100644 --- a/compcheck/comp_check.go +++ b/compcheck/comp_check.go @@ -62,24 +62,25 @@ func NewCompCheckError(err error, errCode int) *CompCheckError { // The input format for the comptible check type CompCheck struct { - NodeId string `json:"node_id,omitempty"` - NodeArch string `json:"node_arch,omitempty"` - NodeType string `json:"node_type,omitempty"` // can be omitted if node_id is specified - NodeClusterNS string `json:"node_cluster_namespace,omitempty"` // can be omitted if node_id is specified. If node_id is not specified the default values is "openhorizon-gent". The value is ignored if the node type is device. - NodeOrg string `json:"node_org,omitempty"` // can be omitted if node_id is specified - NodePolicy *exchangecommon.NodePolicy `json:"node_policy,omitempty"` - NodeUserInput []policy.UserInput `json:"node_user_input,omitempty"` - BusinessPolId string `json:"business_policy_id,omitempty"` - BusinessPolicy *businesspolicy.BusinessPolicy `json:"business_policy,omitempty"` - PatternId string `json:"pattern_id,omitempty"` - Pattern common.AbstractPatternFile `json:"pattern,omitempty"` - ServicePolicy *externalpolicy.ExternalPolicy `json:"service_policy,omitempty"` - Service []common.AbstractServiceFile `json:"service,omitempty"` + NodeId string `json:"node_id,omitempty"` + NodeArch string `json:"node_arch,omitempty"` + NodeType string `json:"node_type,omitempty"` // can be omitted if node_id is specified + NodeClusterNS string `json:"node_cluster_namespace,omitempty"` // can be omitted if node_id is specified. If node_id is not specified the default values is "openhorizon-gent". The value is ignored if the node type is device. + NodeNamespaceScoped bool `json:"node_namespace_scoped,omitempty"` + NodeOrg string `json:"node_org,omitempty"` // can be omitted if node_id is specified + NodePolicy *exchangecommon.NodePolicy `json:"node_policy,omitempty"` + NodeUserInput []policy.UserInput `json:"node_user_input,omitempty"` + BusinessPolId string `json:"business_policy_id,omitempty"` + BusinessPolicy *businesspolicy.BusinessPolicy `json:"business_policy,omitempty"` + PatternId string `json:"pattern_id,omitempty"` + Pattern common.AbstractPatternFile `json:"pattern,omitempty"` + ServicePolicy *externalpolicy.ExternalPolicy `json:"service_policy,omitempty"` + Service []common.AbstractServiceFile `json:"service,omitempty"` } func (p CompCheck) String() string { - return fmt.Sprintf("NodeId: %v, NodeArch: %v, NodeType: %v, NodeClusterNS:%v, NodePolicy: %v, NodeUserInput: %v, BusinessPolId: %v, BusinessPolicy: %v, PatternId: %v, Pattern: %v, ServicePolicy: %v, Service: %v", - p.NodeId, p.NodeArch, p.NodeType, p.NodeClusterNS, p.NodePolicy, p.NodeUserInput, p.BusinessPolId, p.BusinessPolicy, p.PatternId, p.Pattern, p.ServicePolicy, p.Service) + return fmt.Sprintf("NodeId: %v, NodeArch: %v, NodeType: %v, NodeClusterNS: %v, NodeIsNamespaceScoped: %v, NodePolicy: %v, NodeUserInput: %v, BusinessPolId: %v, BusinessPolicy: %v, PatternId: %v, Pattern: %v, ServicePolicy: %v, Service: %v", + p.NodeId, p.NodeArch, p.NodeType, p.NodeClusterNS, p.NodeNamespaceScoped, p.NodePolicy, p.NodeUserInput, p.BusinessPolId, p.BusinessPolicy, p.PatternId, p.Pattern, p.ServicePolicy, p.Service) } @@ -89,19 +90,20 @@ func (p CompCheck) String() string { // This is because the original structures contain interfaces AbstractPatternFile and // AbstractServiceFile that cannot be demarshaled into. type CompCheck_NoAbstract struct { - NodeId string `json:"node_id,omitempty"` - NodeArch string `json:"node_arch,omitempty"` - NodeType string `json:"node_type,omitempty"` // can be omitted if node_id is specified - NodeClusterNS string `json:"node_cluster_namespace,omitempty"` // can be omitted if node_id is specified. If node_id is not specified the default values is "openhorizon-gent". The value is ignored if the node type is device. - NodeOrg string `json:"node_org,omitempty"` // can be omitted if node_id is specified - NodePolicy *exchangecommon.NodePolicy `json:"node_policy,omitempty"` - NodeUserInput []policy.UserInput `json:"node_user_input,omitempty"` - BusinessPolId string `json:"business_policy_id,omitempty"` - BusinessPolicy *businesspolicy.BusinessPolicy `json:"business_policy,omitempty"` - PatternId string `json:"pattern_id,omitempty"` - Pattern *common.PatternFile `json:"pattern,omitempty"` - ServicePolicy *externalpolicy.ExternalPolicy `json:"service_policy,omitempty"` - Service []common.ServiceFile `json:"service,omitempty"` + NodeId string `json:"node_id,omitempty"` + NodeArch string `json:"node_arch,omitempty"` + NodeType string `json:"node_type,omitempty"` // can be omitted if node_id is specified + NodeClusterNS string `json:"node_cluster_namespace,omitempty"` // can be omitted if node_id is specified. If node_id is not specified the default values is "openhorizon-gent". The value is ignored if the node type is device. + NodeNamespaceScoped bool `json:"node_namespace_scoped,omitempty"` + NodeOrg string `json:"node_org,omitempty"` // can be omitted if node_id is specified + NodePolicy *exchangecommon.NodePolicy `json:"node_policy,omitempty"` + NodeUserInput []policy.UserInput `json:"node_user_input,omitempty"` + BusinessPolId string `json:"business_policy_id,omitempty"` + BusinessPolicy *businesspolicy.BusinessPolicy `json:"business_policy,omitempty"` + PatternId string `json:"pattern_id,omitempty"` + Pattern *common.PatternFile `json:"pattern,omitempty"` + ServicePolicy *externalpolicy.ExternalPolicy `json:"service_policy,omitempty"` + Service []common.ServiceFile `json:"service,omitempty"` } // unmashal handler for CompCheck object to handle AbstractPatternFile and AbstractServiceFile @@ -116,6 +118,7 @@ func (p *CompCheck) UnmarshalJSON(b []byte) error { p.NodeArch = cc.NodeArch p.NodeType = cc.NodeType p.NodeClusterNS = cc.NodeClusterNS + p.NodeNamespaceScoped = cc.NodeNamespaceScoped p.NodeOrg = cc.NodeOrg p.NodePolicy = cc.NodePolicy p.NodeUserInput = cc.NodeUserInput @@ -162,20 +165,21 @@ func NewCompCheckOutput(compatible bool, reason map[string]string, input *CompCh // To store the resource (pattern, bp, services etc) used for compatibility check type CompCheckResource struct { - NodeId string `json:"node_id,omitempty"` - NodeArch string `json:"node_arch,omitempty"` - NodeType string `json:"node_type,omitempty"` - NodeClusterNS string `json:"node_cluster_namespace,omitempty"` // can be omitted if node_id is specified. If node_id is not specified the default values is "openhorizon-gent". The value is ignored if the node type is device. - NodeOrg string `json:"node_org,omitempty"` - NodePolicy *exchangecommon.NodePolicy `json:"node_policy,omitempty"` - NodeUserInput []policy.UserInput `json:"node_user_input,omitempty"` - BusinessPolId string `json:"business_policy_id,omitempty"` - BusinessPolicy *businesspolicy.BusinessPolicy `json:"business_policy,omitempty"` - PatternId string `json:"pattern_id,omitempty"` - Pattern common.AbstractPatternFile `json:"pattern,omitempty"` - ServicePolicy map[string]externalpolicy.ExternalPolicy `json:"service_policy,omitempty"` - Service []common.AbstractServiceFile `json:"service,omitempty"` - DepServices map[string]exchange.ServiceDefinition `json:"dependent_services,omitempty"` // for internal use for performance. A map of service definition keyed by id. + NodeId string `json:"node_id,omitempty"` + NodeArch string `json:"node_arch,omitempty"` + NodeType string `json:"node_type,omitempty"` + NodeClusterNS string `json:"node_cluster_namespace,omitempty"` // can be omitted if node_id is specified. If node_id is not specified the default values is "openhorizon-gent". The value is ignored if the node type is device. + NodeNamespaceScoped bool `json:"node_namespace_scoped,omitempty"` // NodeNamespaceScoped false will be omit + NodeOrg string `json:"node_org,omitempty"` + NodePolicy *exchangecommon.NodePolicy `json:"node_policy,omitempty"` + NodeUserInput []policy.UserInput `json:"node_user_input,omitempty"` + BusinessPolId string `json:"business_policy_id,omitempty"` + BusinessPolicy *businesspolicy.BusinessPolicy `json:"business_policy,omitempty"` + PatternId string `json:"pattern_id,omitempty"` + Pattern common.AbstractPatternFile `json:"pattern,omitempty"` + ServicePolicy map[string]externalpolicy.ExternalPolicy `json:"service_policy,omitempty"` + Service []common.AbstractServiceFile `json:"service,omitempty"` + DepServices map[string]exchange.ServiceDefinition `json:"dependent_services,omitempty"` // for internal use for performance. A map of service definition keyed by id. // It is either empty or provides ALL the dependent services needed. It is expected the top level service definitions are provided // in the 'Service' attribute when this attribute is not empty. NeededSB []exchangecommon.SecretBinding `json:"needed_secret_binding,omitempty"` @@ -183,8 +187,8 @@ type CompCheckResource struct { } func (p CompCheckResource) String() string { - return fmt.Sprintf("NodeId: %v, NodeArch: %v, NodeType: %v, NodeClusterNS: %v, NodePolicy: %v, NodeUserInput: %v, BusinessPolId: %v, BusinessPolicy: %v, PatternId: %v, Pattern: %v, ServicePolicy: %v, Service: %v", - p.NodeId, p.NodeArch, p.NodeType, p.NodeClusterNS, p.NodePolicy, p.NodeUserInput, p.BusinessPolId, p.BusinessPolicy, p.PatternId, p.Pattern, p.ServicePolicy, p.Service) + return fmt.Sprintf("NodeId: %v, NodeArch: %v, NodeType: %v, NodeClusterNS: %v, NodeNamespaceScoped: %v, NodePolicy: %v, NodeUserInput: %v, BusinessPolId: %v, BusinessPolicy: %v, PatternId: %v, Pattern: %v, ServicePolicy: %v, Service: %v", + p.NodeId, p.NodeArch, p.NodeType, p.NodeClusterNS, p.NodeNamespaceScoped, p.NodePolicy, p.NodeUserInput, p.BusinessPolId, p.BusinessPolicy, p.PatternId, p.Pattern, p.ServicePolicy, p.Service) } @@ -194,6 +198,7 @@ func NewCompCheckResourceFromUICheck(uiInput *UserInputCheck) *CompCheckResource rsrc.NodeArch = uiInput.NodeArch rsrc.NodeType = uiInput.NodeType rsrc.NodeClusterNS = uiInput.NodeClusterNS + rsrc.NodeNamespaceScoped = uiInput.NodeNamespaceScoped rsrc.NodeUserInput = uiInput.NodeUserInput rsrc.BusinessPolId = uiInput.BusinessPolId rsrc.BusinessPolicy = uiInput.BusinessPolicy @@ -258,6 +263,7 @@ func NewCompCheckResourceFromPolicyCheck(pcInput *PolicyCheck) *CompCheckResourc rsrc.NodeArch = pcInput.NodeArch rsrc.NodeType = pcInput.NodeType rsrc.NodeClusterNS = pcInput.NodeClusterNS + rsrc.NodeNamespaceScoped = pcInput.NodeNamespaceScoped rsrc.NodePolicy = pcInput.NodePolicy rsrc.BusinessPolId = pcInput.BusinessPolId rsrc.BusinessPolicy = pcInput.BusinessPolicy @@ -348,6 +354,7 @@ func deployCompatible(getDeviceHandler exchange.DeviceHandler, return pcOutput, nil } } else if ccInput.NodeId != "" || ccInput.NodePolicy != nil { + ccResource := *privOutput.Input privOutput, err := EvaluatePatternPrivilegeCompatability(serviceDefResolverHandler, getPatterns, nodePolicyHandler, ccInput, privOutput.Input, msgPrinter, true, true) if err != nil { return nil, err @@ -355,6 +362,14 @@ func deployCompatible(getDeviceHandler exchange.DeviceHandler, if !privOutput.Compatible { return createCompCheckOutput(pcOutput, privOutput, nil, nil, checkAllSvcs, msgPrinter), nil } + + privOutput, err = patternClusterNSCompatible(serviceDefResolverHandler, getPatterns, ccInput, &ccResource, true, msgPrinter) + if err != nil { + return nil, err + } + if !privOutput.Compatible { + return createCompCheckOutput(pcOutput, privOutput, nil, nil, checkAllSvcs, msgPrinter), nil + } } // check user input for those services that are compatible @@ -396,6 +411,7 @@ func createUserInputCheckInput(ccInput *CompCheck, pcOutput *CompCheckOutput, ms uiCheckInput.NodeArch = pcOutput.Input.NodeArch uiCheckInput.NodeType = pcOutput.Input.NodeType uiCheckInput.NodeClusterNS = pcOutput.Input.NodeClusterNS + uiCheckInput.NodeNamespaceScoped = pcOutput.Input.NodeNamespaceScoped uiCheckInput.BusinessPolId = pcOutput.Input.BusinessPolId uiCheckInput.BusinessPolicy = pcOutput.Input.BusinessPolicy @@ -533,6 +549,7 @@ func createCompCheckOutput(pcOutput *CompCheckOutput, privOutput *CompCheckOutpu ccInput.NodeArch = lastOutput.Input.NodeArch ccInput.NodeType = lastOutput.Input.NodeType ccInput.NodeClusterNS = lastOutput.Input.NodeClusterNS + ccInput.NodeNamespaceScoped = lastOutput.Input.NodeNamespaceScoped ccInput.NodeOrg = lastOutput.Input.NodeOrg if uiOutput != nil { ccInput.NodeUserInput = uiOutput.Input.NodeUserInput @@ -562,6 +579,7 @@ func convertToPolicyCheck(in *CompCheck) *PolicyCheck { out.NodeArch = in.NodeArch out.NodeType = in.NodeType out.NodeClusterNS = in.NodeClusterNS + out.NodeNamespaceScoped = in.NodeNamespaceScoped out.NodePolicy = in.NodePolicy out.BusinessPolId = in.BusinessPolId out.BusinessPolicy = in.BusinessPolicy @@ -801,7 +819,7 @@ func VerifyNodeClusterNamespace(nodeNamespace string, exchNodeNamespace string, if nodeNamespace != "" { if exchNodeNamespace != "" && nodeNamespace != exchNodeNamespace { - return "", NewCompCheckError(fmt.Errorf(msgPrinter.Sprintf("The input node's cluster namespace '%v' does not match the node's cluster namespace '%v' from the node %v.", nodeNamespace, exchNodeNamespace, nodeId)), COMPCHECK_INPUT_ERROR) + return "", NewCompCheckError(fmt.Errorf(msgPrinter.Sprintf("The input node's cluster namespace '%v' does not match the node's cluster namespace '%v' from the exchange %v.", nodeNamespace, exchNodeNamespace, nodeId)), COMPCHECK_INPUT_ERROR) } return nodeNamespace, nil } else { @@ -809,14 +827,23 @@ func VerifyNodeClusterNamespace(nodeNamespace string, exchNodeNamespace string, } } -// Check if the node namespace is compatible with the serivce for cluster case. -// The deployerNamespace is the namespace defined in the deployment policy or pattern. If it is empty, it will be -// the namespace defined in the clusterDeployment attribute of service definition. +func VerifyNodeScope(nodeIsNamespaceScope bool, exchNodeIsNamespaceScope bool, nodeId string, msgPrinter *message.Printer) (bool, error) { + if msgPrinter == nil { + msgPrinter = i18n.GetMessagePrinter() + } + + if nodeIsNamespaceScope != exchNodeIsNamespaceScope { + return false, NewCompCheckError(fmt.Errorf(msgPrinter.Sprintf("The input node's scope '%v' does not match the node's scope '%v' from the exchange %v.", nodeIsNamespaceScope, exchNodeIsNamespaceScope, nodeId)), COMPCHECK_INPUT_ERROR) + } + // nodeIsNamespaceScope == exchNodeIsNamespaceScope + return nodeIsNamespaceScope, nil +} + +// Check if the node scope and node namespace is compatible with the serivce for cluster case. // Compatibility check: -// If the node's namespace is openhorizon-agent, then it can deploy the services with all namespaces. +// If the node is cluster scoped, then it can deploy the services with all namespaces. // Otherwise it can only deploy the service to its own namespace. -// If the service's namepace is empty, then it will be deployed to the same namespace as the agent. -func CheckClusterNamespaceCompatibility(nodeType string, nodeNamespace string, deployerNamespace string, clusterDeployment interface{}, inspectOperator bool, msgPrinter *message.Printer) (bool, string, string) { +func CheckClusterNamespaceCompatibility(nodeType string, nodeNamespace string, nodeIsNamespaceScoped bool, deployerNamespace string, clusterDeployment interface{}, patternId string, inspectOperator bool, msgPrinter *message.Printer) (bool, string, string) { if msgPrinter == nil { msgPrinter = i18n.GetMessagePrinter() } @@ -832,9 +859,31 @@ func CheckClusterNamespaceCompatibility(nodeType string, nodeNamespace string, d } } + // check the pattern clusterNamespace against cluster node scope. Rule is: + // - Cluster scoped nodes only deploy patterns that have an empty "clusterNamespace" field. + // - A pattern with an empty "clusterNamespace" MUST NOT be deployed to a namespace scoped node. + if patternId != "" { + // pattern case + if err := ValidatePatternClusterNamespace(nodeIsNamespaceScoped, nodeNamespace, deployerNamespace, patternId, msgPrinter); err != nil { + return false, "", err.Error() + } + } + // when pattern case reach here, it should be one of these 2 scenarios: + // 1. cluster scope && clusterNamespace == "" => continue to check svc namespace + // 2. namespace scope && clusterNamespaceInPattern == NodeNamespace => deploy to deployNamespace/nodeNamespace + // the case where the namespace in the deployment policy or pattern is defined. if deployerNamespace != "" { - if nodeNamespace == deployerNamespace || nodeNamespace == externalpolicy.DEFAULT_NODE_K8S_NAMESPACE { + // YES: deploy to ABC + // policy case: + // - nodeNamespace: ABC, cluster/namespaced scope, deployment policy has "clusterNamespace":"ABC" + // - nodeNamespace: EFG, cluster scope, deployment policy has "clusterNamespace":"ABC" + // pattern case: + // - nodeNamespace: ABC, namespace scoped, pattern has "clusterNamespace":"ABC" + // NO: + // policy case: + // - nodeNamespace: EFG, namespace-scoped, deployment policy has "clusterNamespace":"ABC" + if nodeNamespace == deployerNamespace || !nodeIsNamespaceScoped { return true, deployerNamespace, "" } else { return false, deployerNamespace, msgPrinter.Sprintf("The namespace '%v' specified in the deployment policy or pattern does not match the namespace of the agent: '%v'", deployerNamespace, nodeNamespace) @@ -852,17 +901,101 @@ func CheckClusterNamespaceCompatibility(nodeType string, nodeNamespace string, d } // the case where the namespace in the deployment policy or pattern is not defined. - if nodeNamespace == externalpolicy.DEFAULT_NODE_K8S_NAMESPACE { - return true, svcNamespace, "" - } else { - if nodeNamespace == svcNamespace { + if !nodeIsNamespaceScoped { + if svcNamespace != "" { return true, svcNamespace, "" + } else { + return true, nodeNamespace, "" + } + } else { + if svcNamespace == "" || nodeNamespace == svcNamespace { + return true, nodeNamespace, "" } else { return false, svcNamespace, msgPrinter.Sprintf("The namespace '%v' specified in the service does not match the namespace of the agent: '%v'. Please overwrite it in the deployment policy or pattern.", svcNamespace, nodeNamespace) } } } +// return error if +// 1. namespace scoped agent: clusterNamespce in pattern is "" or is different from node namespace +// 2. cluster scoped agent: clusterNamespace in pattern is not "" +func ValidatePatternClusterNamespace(isNamespaceScoped bool, nodeNamespace string, patternNamespace string, patternId string, msgPrinter *message.Printer) error { + if msgPrinter == nil { + msgPrinter = i18n.GetMessagePrinter() + } + if isNamespaceScoped { + if patternNamespace == "" || patternNamespace != "" && patternNamespace != nodeNamespace { + return NewCompCheckError(fmt.Errorf(msgPrinter.Sprintf("The namespace '%v' specified in the pattern '%v' is empty or does not match the namespace of the namespace scoped agent: '%v'. Namespace scoped agent is only compatible with pattern that has the same namespace with agent.", patternNamespace, patternId, nodeNamespace)), COMPCHECK_VALIDATION_ERROR) + } + } else { + // is cluster scope + if patternNamespace != "" { + return NewCompCheckError(fmt.Errorf(msgPrinter.Sprintf("The Cluster namespace specified in the pattern '%v' is %v, only pattern with empty clsuter namespace can be registered for cluster scoped agent", patternId, patternNamespace)), COMPCHECK_VALIDATION_ERROR) + } + } + return nil +} + +// getDeviceHandler, getBusinessPolicies, getPatterns, true, msgPrinter +func patternClusterNSCompatible(serviceDefResolverHandler exchange.ServiceDefResolverHandler, + getPatterns exchange.PatternHandler, + ccInput *CompCheck, ccResource *CompCheckResource, + inspectOperator bool, msgPrinter *message.Printer) (*CompCheckOutput, error) { + + // get default message printer if nil + if msgPrinter == nil { + msgPrinter = i18n.GetMessagePrinter() + } + + patternDef, err := processPattern(getPatterns, ccInput.PatternId, ccInput.Pattern, msgPrinter) + if err != nil { + return nil, err + } + + patternClusterNS := patternDef.GetClusterNamespace() + if ccInput.NodeType == persistence.DEVICE_TYPE_DEVICE { + return NewCompCheckOutput(true, nil, nil), nil + } + + compatible := true + reason := "" + if !ccInput.NodeNamespaceScoped { + var clusterDeployment interface{} + // for cluster scoped agent, no need to check embedded svc namespace + compatible, _, reason = CheckClusterNamespaceCompatibility(ccInput.NodeType, ccInput.NodeClusterNS, ccInput.NodeNamespaceScoped, patternClusterNS, clusterDeployment, ccInput.PatternId, false, msgPrinter) + if !compatible { + return NewCompCheckOutput(false, map[string]string{ccInput.PatternId: fmt.Sprintf("Cluster namespace %v in this pattern is incompatible: %v", patternClusterNS, reason)}, nil), nil + } else { + return NewCompCheckOutput(compatible, nil, ccResource), nil + } + } + + // check cluster namespace compatibility for namespace scoped agent + messages := map[string]string{} + for _, svcRef := range getWorkloadsFromPattern(patternDef, ccResource.NodeArch) { + for _, workload := range svcRef.ServiceVersions { + topSvc, topId, _, _ := GetServiceAndDeps(svcRef.ServiceURL, svcRef.ServiceOrg, workload.Version, svcRef.ServiceArch, ccInput.Service, + ccResource.DepServices, serviceDefResolverHandler, msgPrinter) + + if err != nil { + return nil, err + } + + compatible, _, reason = CheckClusterNamespaceCompatibility(ccInput.NodeType, ccInput.NodeClusterNS, ccInput.NodeNamespaceScoped, patternClusterNS, topSvc.GetClusterDeployment(), ccInput.PatternId, true, msgPrinter) + if !compatible { + messages[topId] = fmt.Sprintf("Cluster namespace is incompatible, reason: %v", reason) + } + } + } + if len(messages) == 0 { + // no incompatible entry + return NewCompCheckOutput(true, nil, ccResource), nil + } + + return NewCompCheckOutput(false, messages, ccResource), nil + +} + // Get the dependent services for the given service. // It goes to the dependentServices to find a dependent first. If not found // it will go to the exchange to get the dependents. diff --git a/compcheck/policy_check.go b/compcheck/policy_check.go index 4e3406a87..c2a58a780 100644 --- a/compcheck/policy_check.go +++ b/compcheck/policy_check.go @@ -18,23 +18,24 @@ import ( // The input format for the policy check type PolicyCheck struct { - NodeId string `json:"node_id,omitempty"` - NodeArch string `json:"node_arch,omitempty"` - NodeType string `json:"node_type,omitempty"` // can be omitted if node_id is specified - NodeClusterNS string `json:"node_cluster_namespace,omitempty"` // can be omitted if node_id is specified. If node_id is not specified the default values is "openhorizon-gent". The value is ignored if the node type is device. - NodePolicy *exchangecommon.NodePolicy `json:"node_policy,omitempty"` - BusinessPolId string `json:"business_policy_id,omitempty"` - BusinessPolicy *businesspolicy.BusinessPolicy `json:"business_policy,omitempty"` - ServicePolicy *externalpolicy.ExternalPolicy `json:"service_policy,omitempty"` - Service []common.AbstractServiceFile `json:"service,omitempty"` //only needed if the services are not in the exchange - DepServices map[string]exchange.ServiceDefinition `json:"dependent_services,omitempty"` // for internal use for performance. A map of service definition keyed by id. + NodeId string `json:"node_id,omitempty"` + NodeArch string `json:"node_arch,omitempty"` + NodeType string `json:"node_type,omitempty"` // can be omitted if node_id is specified + NodeClusterNS string `json:"node_cluster_namespace,omitempty"` // can be omitted if node_id is specified. If node_id is not specified the default values is "openhorizon-gent". The value is ignored if the node type is device. + NodeNamespaceScoped bool `json:"node_namespace_scoped,omitempty"` + NodePolicy *exchangecommon.NodePolicy `json:"node_policy,omitempty"` + BusinessPolId string `json:"business_policy_id,omitempty"` + BusinessPolicy *businesspolicy.BusinessPolicy `json:"business_policy,omitempty"` + ServicePolicy *externalpolicy.ExternalPolicy `json:"service_policy,omitempty"` + Service []common.AbstractServiceFile `json:"service,omitempty"` //only needed if the services are not in the exchange + DepServices map[string]exchange.ServiceDefinition `json:"dependent_services,omitempty"` // for internal use for performance. A map of service definition keyed by id. // It is either empty or provides ALL the dependent services needed. It is expected the top level service definitions are provided // in the 'Service' attribute when this attribute is not empty. } func (p PolicyCheck) String() string { - return fmt.Sprintf("NodeId: %v, NodeArch: %v, NodeType: %v, NodeClusterNS: %v, NodePolicy: %v, BusinessPolId: %v, BusinessPolicy: %v, ServicePolicy: %v, Service:%v", - p.NodeId, p.NodeArch, p.NodeType, p.NodeClusterNS, p.NodePolicy, p.BusinessPolId, p.BusinessPolicy, p.ServicePolicy, p.Service) + return fmt.Sprintf("NodeId: %v, NodeArch: %v, NodeType: %v, NodeClusterNS: %v, NodeNamespaceScoped: %v, NodePolicy: %v, BusinessPolId: %v, BusinessPolicy: %v, ServicePolicy: %v, Service:%v", + p.NodeId, p.NodeArch, p.NodeType, p.NodeClusterNS, p.NodeNamespaceScoped, p.NodePolicy, p.BusinessPolId, p.BusinessPolicy, p.ServicePolicy, p.Service) } // unmashal handler for PolicyCheck object to handle AbstractPatternFile and AbstractServiceFile @@ -49,6 +50,7 @@ func (p *PolicyCheck) UnmarshalJSON(b []byte) error { p.NodeArch = cc.NodeArch p.NodeType = cc.NodeType p.NodeClusterNS = cc.NodeClusterNS + p.NodeNamespaceScoped = cc.NodeNamespaceScoped p.NodePolicy = cc.NodePolicy p.BusinessPolId = cc.BusinessPolId p.BusinessPolicy = cc.BusinessPolicy @@ -131,6 +133,7 @@ func policyCompatible(getDeviceHandler exchange.DeviceHandler, resources.NodeType = node.NodeType resources.NodeClusterNS = node.ClusterNamespace + resources.NodeNamespaceScoped = node.IsNamespaceScoped // resources.NodeNamespaceScoped will be false if node.IsNamespaceScoped is false or not set(omit) } // verify the input node type value and get the node type from @@ -149,6 +152,13 @@ func policyCompatible(getDeviceHandler exchange.DeviceHandler, resources.NodeClusterNS = nodeClusterNS } + // verify the input node scope and exchange node scope are same + if nodeIsNamespaceScope, err := VerifyNodeScope(input.NodeNamespaceScoped, resources.NodeNamespaceScoped, nodeId, msgPrinter); err != nil { + return nil, err + } else { + resources.NodeNamespaceScoped = nodeIsNamespaceScope + } + // validate node policy and convert it to internal policy var nPolicy *policy.Policy var err1 error @@ -235,7 +245,7 @@ func policyCompatible(getDeviceHandler exchange.DeviceHandler, if compatible { // check namespace compatibility if resources.NodeType == persistence.DEVICE_TYPE_CLUSTER { - compatible, _, reason = CheckClusterNamespaceCompatibility(resources.NodeType, resources.NodeClusterNS, bPolicy.ClusterNamespace, topSvcDef.GetClusterDeployment(), true, msgPrinter) + compatible, _, reason = CheckClusterNamespaceCompatibility(resources.NodeType, resources.NodeClusterNS, resources.NodeNamespaceScoped, bPolicy.ClusterNamespace, topSvcDef.GetClusterDeployment(), bPolicy.PatternId, true, msgPrinter) } if compatible { // policy compatibility check @@ -286,7 +296,7 @@ func policyCompatible(getDeviceHandler exchange.DeviceHandler, if compatible { // check namespace compatibility if resources.NodeType == persistence.DEVICE_TYPE_CLUSTER { - compatible, _, reason = CheckClusterNamespaceCompatibility(resources.NodeType, resources.NodeClusterNS, bPolicy.ClusterNamespace, topSvcDef.GetClusterDeployment(), true, msgPrinter) + compatible, _, reason = CheckClusterNamespaceCompatibility(resources.NodeType, resources.NodeClusterNS, resources.NodeNamespaceScoped, bPolicy.ClusterNamespace, topSvcDef.GetClusterDeployment(), bPolicy.PatternId, true, msgPrinter) } if compatible { // policy compatibility check @@ -344,7 +354,7 @@ func policyCompatible(getDeviceHandler exchange.DeviceHandler, if compatible { // check namespace compatibility if resources.NodeType == persistence.DEVICE_TYPE_CLUSTER { - compatible, _, reason = CheckClusterNamespaceCompatibility(resources.NodeType, resources.NodeClusterNS, bPolicy.ClusterNamespace, topSvcDef.GetClusterDeployment(), true, msgPrinter) + compatible, _, reason = CheckClusterNamespaceCompatibility(resources.NodeType, resources.NodeClusterNS, resources.NodeNamespaceScoped, bPolicy.ClusterNamespace, topSvcDef.GetClusterDeployment(), bPolicy.PatternId, true, msgPrinter) } if compatible { // policy compatibility check diff --git a/compcheck/userinput_check.go b/compcheck/userinput_check.go index 030a73892..46256a210 100644 --- a/compcheck/userinput_check.go +++ b/compcheck/userinput_check.go @@ -17,25 +17,26 @@ import ( // The input format for the userinput check type UserInputCheck struct { - NodeId string `json:"node_id,omitempty"` - NodeArch string `json:"node_arch,omitempty"` - NodeType string `json:"node_type,omitempty"` // can be omitted if node_id is specified - NodeClusterNS string `json:"node_cluster_namespace,omitempty"` // can be omitted if node_id is specified. If node_id is not specified the default values is "openhorizon-gent". The value is ignored if the node type is device. - NodeUserInput []policy.UserInput `json:"node_user_input,omitempty"` - BusinessPolId string `json:"business_policy_id,omitempty"` - BusinessPolicy *businesspolicy.BusinessPolicy `json:"business_policy,omitempty"` - PatternId string `json:"pattern_id,omitempty"` - Pattern common.AbstractPatternFile `json:"pattern,omitempty"` - Service []common.AbstractServiceFile `json:"service,omitempty"` - ServiceToCheck []string `json:"service_to_check,omitempty"` // for internal use for performance. only check the service with the ids. If empty, check all. - DepServices map[string]exchange.ServiceDefinition `json:"dependent_services,omitempty"` // for internal use for performance. A map of service definition keyed by id. + NodeId string `json:"node_id,omitempty"` + NodeArch string `json:"node_arch,omitempty"` + NodeType string `json:"node_type,omitempty"` // can be omitted if node_id is specified + NodeClusterNS string `json:"node_cluster_namespace,omitempty"` // can be omitted if node_id is specified. If node_id is not specified the default values is "openhorizon-gent". The value is ignored if the node type is device. + NodeNamespaceScoped bool `json:"node_namespace_scoped,omitempty"` // Namespace-scoped: false will be omit + NodeUserInput []policy.UserInput `json:"node_user_input,omitempty"` + BusinessPolId string `json:"business_policy_id,omitempty"` + BusinessPolicy *businesspolicy.BusinessPolicy `json:"business_policy,omitempty"` + PatternId string `json:"pattern_id,omitempty"` + Pattern common.AbstractPatternFile `json:"pattern,omitempty"` + Service []common.AbstractServiceFile `json:"service,omitempty"` + ServiceToCheck []string `json:"service_to_check,omitempty"` // for internal use for performance. only check the service with the ids. If empty, check all. + DepServices map[string]exchange.ServiceDefinition `json:"dependent_services,omitempty"` // for internal use for performance. A map of service definition keyed by id. // It is either empty or provides ALL the dependent services needed. It is expected the top level service definitions are provided // in the 'Service' attribute when this attribute is not empty. } func (p UserInputCheck) String() string { - return fmt.Sprintf("NodeId: %v, NodeArch: %v, NodeType: %v, NodeClusterNS: %v, NodeUserInput: %v, BusinessPolId: %v, BusinessPolicy: %v, PatternId: %v, Pattern: %v, Service: %v,", - p.NodeId, p.NodeArch, p.NodeType, p.NodeClusterNS, p.NodeUserInput, p.BusinessPolId, p.BusinessPolicy, p.PatternId, p.Pattern, p.Service) + return fmt.Sprintf("NodeId: %v, NodeArch: %v, NodeType: %v, NodeClusterNS: %v, NodeNamespaceScoped: %v, NodeUserInput: %v, BusinessPolId: %v, BusinessPolicy: %v, PatternId: %v, Pattern: %v, Service: %v,", + p.NodeId, p.NodeArch, p.NodeType, p.NodeClusterNS, p.NodeNamespaceScoped, p.NodeUserInput, p.BusinessPolId, p.BusinessPolicy, p.PatternId, p.Pattern, p.Service) } // unmashal handler for UserInputCheck object to handle AbstractPatternFile and AbstractServiceFile @@ -50,6 +51,7 @@ func (p *UserInputCheck) UnmarshalJSON(b []byte) error { p.NodeArch = cc.NodeArch p.NodeType = cc.NodeType p.NodeClusterNS = cc.NodeClusterNS + p.NodeNamespaceScoped = cc.NodeNamespaceScoped p.NodeUserInput = cc.NodeUserInput p.BusinessPolId = cc.BusinessPolId p.BusinessPolicy = cc.BusinessPolicy @@ -142,6 +144,7 @@ func userInputCompatible(getDeviceHandler exchange.DeviceHandler, resources.NodeType = node.NodeType resources.NodeClusterNS = node.ClusterNamespace + resources.NodeNamespaceScoped = node.IsNamespaceScoped if input.NodeUserInput == nil { resources.NodeUserInput = node.UserInput @@ -164,6 +167,13 @@ func userInputCompatible(getDeviceHandler exchange.DeviceHandler, resources.NodeClusterNS = nodeClusterNS } + // verify the input node scope and exchange node scope are same + if nodeIsNamespaceScope, err := VerifyNodeScope(input.NodeNamespaceScoped, resources.NodeNamespaceScoped, nodeId, msgPrinter); err != nil { + return nil, err + } else { + resources.NodeNamespaceScoped = nodeIsNamespaceScope + } + // make sure only specify one: business policy or pattern useBPol := false if input.BusinessPolId != "" || input.BusinessPolicy != nil { @@ -260,7 +270,7 @@ func userInputCompatible(getDeviceHandler exchange.DeviceHandler, reason = reason_t svc_other_mismatch[sId] = true } else if resources.NodeType == persistence.DEVICE_TYPE_CLUSTER { - compatible_n, _, reason_n := CheckClusterNamespaceCompatibility(resources.NodeType, resources.NodeClusterNS, consumerNamespace, topSvcDef.GetClusterDeployment(), true, msgPrinter) + compatible_n, _, reason_n := CheckClusterNamespaceCompatibility(resources.NodeType, resources.NodeClusterNS, resources.NodeNamespaceScoped, consumerNamespace, topSvcDef.GetClusterDeployment(), input.PatternId, true, msgPrinter) if !compatible_n { reason = reason_n svc_other_mismatch[sId] = true @@ -305,7 +315,7 @@ func userInputCompatible(getDeviceHandler exchange.DeviceHandler, reason = reason_t svc_other_mismatch[sId] = true } else if resources.NodeType == persistence.DEVICE_TYPE_CLUSTER { - compatible_n, _, reason_n := CheckClusterNamespaceCompatibility(resources.NodeType, resources.NodeClusterNS, consumerNamespace, svc.GetClusterDeployment(), true, msgPrinter) + compatible_n, _, reason_n := CheckClusterNamespaceCompatibility(resources.NodeType, resources.NodeClusterNS, resources.NodeNamespaceScoped, consumerNamespace, svc.GetClusterDeployment(), input.PatternId, true, msgPrinter) if !compatible_n { reason = reason_n svc_other_mismatch[sId] = true @@ -361,7 +371,7 @@ func userInputCompatible(getDeviceHandler exchange.DeviceHandler, reason = reason_t svc_other_mismatch[sId] = true } else if resources.NodeType == persistence.DEVICE_TYPE_CLUSTER { - compatible_n, _, reason_n := CheckClusterNamespaceCompatibility(resources.NodeType, resources.NodeClusterNS, consumerNamespace, useSDef.GetClusterDeployment(), true, msgPrinter) + compatible_n, _, reason_n := CheckClusterNamespaceCompatibility(resources.NodeType, resources.NodeClusterNS, resources.NodeNamespaceScoped, consumerNamespace, useSDef.GetClusterDeployment(), input.PatternId, true, msgPrinter) if !compatible_n { reason = reason_n svc_other_mismatch[sId] = true diff --git a/exchange/node.go b/exchange/node.go index b2bb6bfd6..706d523ae 100644 --- a/exchange/node.go +++ b/exchange/node.go @@ -18,6 +18,7 @@ type Device struct { Owner string `json:"owner"` NodeType string `json:"nodeType"` ClusterNamespace string `json:"clusterNamespace,omitempty"` + IsNamespaceScoped bool `json:"isNamespaceScoped"` Pattern string `json:"pattern"` RegisteredServices []Microservice `json:"registeredServices"` MsgEndPoint string `json:"msgEndPoint"` @@ -32,7 +33,7 @@ type Device struct { } func (d Device) String() string { - return fmt.Sprintf("Name: %v, Owner: %v, NodeType: %v, ClusterNamespace: %v, HAGroup: %v, Pattern: %v, SoftwareVersions: %v, LastHeartbeat: %v, RegisteredServices: %v, MsgEndPoint: %v, Arch: %v, UserInput: %v, HeartbeatIntv: %v", d.Name, d.Owner, d.NodeType, d.ClusterNamespace, d.HAGroup, d.Pattern, d.SoftwareVersions, d.LastHeartbeat, d.RegisteredServices, d.MsgEndPoint, d.Arch, d.UserInput, d.HeartbeatIntv) + return fmt.Sprintf("Name: %v, Owner: %v, NodeType: %v, ClusterNamespace: %v, IsNamespaceScoped: %v, HAGroup: %v, Pattern: %v, SoftwareVersions: %v, LastHeartbeat: %v, RegisteredServices: %v, MsgEndPoint: %v, Arch: %v, UserInput: %v, HeartbeatIntv: %v", d.Name, d.Owner, d.NodeType, d.ClusterNamespace, d.IsNamespaceScoped, d.HAGroup, d.Pattern, d.SoftwareVersions, d.LastHeartbeat, d.RegisteredServices, d.MsgEndPoint, d.Arch, d.UserInput, d.HeartbeatIntv) } func (d Device) ShortString() string { @@ -44,7 +45,7 @@ func (d Device) ShortString() string { } func (n Device) DeepCopy() *Device { - nodeCopy := Device{Token: n.Token, Name: n.Name, Owner: n.Owner, NodeType: n.NodeType, ClusterNamespace: n.ClusterNamespace, HAGroup: n.HAGroup, Pattern: n.Pattern, MsgEndPoint: n.MsgEndPoint, LastHeartbeat: n.LastHeartbeat, PublicKey: n.PublicKey, Arch: n.Arch, HeartbeatIntv: n.HeartbeatIntv, LastUpdated: n.LastUpdated} + nodeCopy := Device{Token: n.Token, Name: n.Name, Owner: n.Owner, NodeType: n.NodeType, ClusterNamespace: n.ClusterNamespace, IsNamespaceScoped: n.IsNamespaceScoped, HAGroup: n.HAGroup, Pattern: n.Pattern, MsgEndPoint: n.MsgEndPoint, LastHeartbeat: n.LastHeartbeat, PublicKey: n.PublicKey, Arch: n.Arch, HeartbeatIntv: n.HeartbeatIntv, LastUpdated: n.LastUpdated} if n.RegisteredServices == nil { nodeCopy.RegisteredServices = nil } else { @@ -189,6 +190,7 @@ type PutDeviceRequest struct { Name string `json:"name"` NodeType string `json:"nodeType"` ClusterNamespace *string `json:"clusterNamespace,omitempty"` + IsNamespaceScoped bool `json:"isNamespaceScoped"` Pattern string `json:"pattern"` RegisteredServices []Microservice `json:"registeredServices"` MsgEndPoint string `json:"msgEndPoint"` @@ -199,7 +201,7 @@ type PutDeviceRequest struct { } func (p PutDeviceRequest) String() string { - return fmt.Sprintf("Token: %v, Name: %v, NodeType: %v, ClusterNamespace: %v, RegisteredServices %v, MsgEndPoint %v, SoftwareVersions %v, PublicKey %x, Arch: %v, UserInput: %v", "*****", p.Name, p.NodeType, p.ClusterNamespace, p.RegisteredServices, p.MsgEndPoint, p.SoftwareVersions, p.PublicKey, p.Arch, p.UserInput) + return fmt.Sprintf("Token: %v, Name: %v, NodeType: %v, ClusterNamespace: %v, IsNamespaceScoped: %v, RegisteredServices %v, MsgEndPoint %v, SoftwareVersions %v, PublicKey %x, Arch: %v, UserInput: %v", "*****", p.Name, p.NodeType, p.ClusterNamespace, p.IsNamespaceScoped, p.RegisteredServices, p.MsgEndPoint, p.SoftwareVersions, p.PublicKey, p.Arch, p.UserInput) } func (p PutDeviceRequest) ShortString() string { @@ -276,6 +278,7 @@ type PatchDeviceRequest struct { Pattern *string `json:"pattern,omitempty"` Arch *string `json:"arch,omitempty"` ClusterNamespace *string `json:"clusterNamespace,omitempty"` + IsNamespaceScoped *bool `json:"isNamespaceScoped,omitempty"` RegisteredServices *[]Microservice `json:"registeredServices,omitempty"` SoftwareVersions SoftwareVersion `json:"softwareVersions"` } @@ -293,7 +296,11 @@ func (p PatchDeviceRequest) String() string { if p.ClusterNamespace != nil { clusterNs = *p.ClusterNamespace } - return fmt.Sprintf("UserInput: %v, RegisteredServices: %v, Pattern: %v, Arch: %v, ClusterNamespace: %v, SoftwareVersions: %v", p.UserInput, p.RegisteredServices, pattern, arch, clusterNs, p.SoftwareVersions) + isNs := "nil" + if p.IsNamespaceScoped != nil { + isNs = fmt.Sprintf("%v", *p.IsNamespaceScoped) + } + return fmt.Sprintf("UserInput: %v, RegisteredServices: %v, Pattern: %v, Arch: %v, ClusterNamespace: %v, IsNamespaceScoped: %v, SoftwareVersions: %v", p.UserInput, p.RegisteredServices, pattern, arch, clusterNs, isNs, p.SoftwareVersions) } func (p PatchDeviceRequest) ShortString() string { @@ -327,7 +334,12 @@ func (p PatchDeviceRequest) ShortString() string { clusterNs = *p.ClusterNamespace } - return fmt.Sprintf("UserInput: %v, RegisteredServices: %v, Pattern: %v, Arch: %v, ClusterNamespace: %v, SoftwareVersions: %v", userInput, registeredServices, pattern, arch, clusterNs, p.SoftwareVersions) + isNs := "nil" + if p.IsNamespaceScoped != nil { + isNs = fmt.Sprintf("%v", *p.IsNamespaceScoped) + } + + return fmt.Sprintf("UserInput: %v, RegisteredServices: %v, Pattern: %v, Arch: %v, ClusterNamespace: %v, IsNamespaceScoped: %v, SoftwareVersions: %v", userInput, registeredServices, pattern, arch, clusterNs, isNs, p.SoftwareVersions) } // patch the the device diff --git a/externalpolicy/constraint_expression.go b/externalpolicy/constraint_expression.go index dc822fb01..d7248029d 100644 --- a/externalpolicy/constraint_expression.go +++ b/externalpolicy/constraint_expression.go @@ -212,3 +212,101 @@ func parseConstraintExpression(constraint string, handler plugin_registry.Constr return nil, constraint, err } + +func getConstraintExpressionWithName(constraint string, handler plugin_registry.ConstraintLanguagePlugin, constrainName string) (map[string]interface{}, string, error) { + var err error + var nextProp string + var ctrlOp string + var subExpr map[string]interface{} + + andArray := make([]interface{}, 0) + orArray := make([]interface{}, 0) + + for err == nil { + // Start of property expression. This case will consume the entire expression. + nextProp, constraint, err = handler.GetNextExpression(constraint) + if err != nil { + return nil, constraint, err + } + if strings.TrimSpace(nextProp) != "" { + prop := strings.Split(nextProp, "\a") + if prop[0] == constrainName { + andArray = append(andArray, *PropertyExpression_Factory(prop[0], strings.TrimSpace(prop[2]), strings.TrimSpace(prop[1]))) + } + } + ctrlOp, constraint, err = handler.GetNextOperator(constraint) + if err != nil { + return nil, constraint, err + } + + if ctrlOp == "(" { + // handle a parenthetical expression as a seperate constraint expression + subExpr, constraint, err = getConstraintExpressionWithName(constraint, handler, constrainName) + if err != nil { + return nil, constraint, err + } + + andArray = append(andArray, subExpr) + + ctrlOp, constraint, err = handler.GetNextOperator(constraint) + if err != nil { + return nil, constraint, err + } + } + if ctrlOp == "||" || ctrlOp == "OR" { + innerAnd := map[string]interface{}{ + OP_AND: andArray, + } + orArray = append(orArray, innerAnd) + andArray = make([]interface{}, 0) + } else if ctrlOp == ")" || ctrlOp == "" { + // end or expression. append andArray to orArray and append the result to allPropArray + innerAnd := map[string]interface{}{ + OP_AND: andArray, + } + orArray = append(orArray, innerAnd) + newRP := map[string]interface{}{OP_OR: orArray} + return newRP, constraint, nil + } + } + return nil, constraint, err +} + +func GetParseConstraintWithName(extConstraint *ConstraintExpression, constraintName string) ([]interface{}, error) { + if extConstraint == nil || len(*extConstraint) == 0 { + return nil, nil + } + + var handler plugin_registry.ConstraintLanguagePlugin + var err error + allPropArray := make([]interface{}, 0) + + for _, remainder := range *extConstraint { + remainder := strings.Replace(remainder, "\a", " ", -1) + + // Get a handle to the specific language handler we will be using. + handler, err = extConstraint.GetLanguageHandler() + if err != nil { + return nil, fmt.Errorf("unable to obtain policy constraint language handler, error %v", err) + } + + // Create a new Required Property structure and initialize it with a top level OR followed by a top level AND. This will allow us + // to drop expressions into the structure as they come in through the GetNextExpression function. + + newPropArray, _, err := getConstraintExpressionWithName(remainder, handler, constraintName) + if err != nil { + return nil, err + } + allPropArray = append(allPropArray, newPropArray) + } + + return allPropArray, err +} + +func ConvertParsedConstraintToRequiredProperty(parsedConstraint []interface{}) *RequiredProperty { + allRP := RequiredProperty_Factory() + allRP.Initialize(&map[string]interface{}{ + OP_AND: parsedConstraint, + }) + return allRP +} diff --git a/governance/governance.go b/governance/governance.go index a50d1cb71..49e38a9a6 100644 --- a/governance/governance.go +++ b/governance/governance.go @@ -2125,6 +2125,4 @@ func (w *GovernanceWorker) GetRequestedClusterNamespaceFromAg(ag *persistence.Es } else { return tcPolicy.ClusterNamespace, nil } - - return "", nil } diff --git a/kube_operator/api_objects.go b/kube_operator/api_objects.go index 2867b9d27..3d626b641 100644 --- a/kube_operator/api_objects.go +++ b/kube_operator/api_objects.go @@ -223,6 +223,13 @@ type NamespaceCoreV1 struct { } func (n NamespaceCoreV1) Install(c KubeClient, namespace string) error { + glog.V(3).Infof(kwlog(fmt.Sprintf("Lily - namespace in operator is %v, service deploy namespace is: %v", n.Name(), namespace))) + // The deploy namespace has been added to the apiObjMap in client.go + if namespace != n.Name() { + glog.Warningf(kwlog(fmt.Sprintf("Embedded namespace '%v' is ignored. Service will be deployed to '%v'.", n.Name(), namespace))) + return nil + } + glog.V(3).Infof(kwlog(fmt.Sprintf("attempting to create namespace %v", n.NamespaceObject))) _, err := c.Client.CoreV1().Namespaces().Create(context.Background(), n.NamespaceObject, metav1.CreateOptions{}) if err != nil { @@ -233,19 +240,20 @@ func (n NamespaceCoreV1) Install(c KubeClient, namespace string) error { } func (n NamespaceCoreV1) Uninstall(c KubeClient, namespace string) { + // embedded namespace is not created if deployernamespace != embedded namespace. if namespace == cutil.GetClusterNamespace() { - glog.V(3).Infof(kwlog(fmt.Sprintf("skipping deletion of namespace used by agent %v", n.NamespaceObject))) + glog.V(3).Infof(kwlog(fmt.Sprintf("skipping deletion of namespace used by agent %v", namespace))) return } - glog.V(3).Infof(kwlog(fmt.Sprintf("deleting namespace %v", n.NamespaceObject))) - err := c.Client.CoreV1().Namespaces().Delete(context.Background(), n.Name(), metav1.DeleteOptions{}) + glog.V(3).Infof(kwlog(fmt.Sprintf("deleting namespace %v", namespace))) + err := c.Client.CoreV1().Namespaces().Delete(context.Background(), namespace, metav1.DeleteOptions{}) if err != nil { - glog.Errorf(kwlog(fmt.Sprintf("unable to delete namespace %s. Error: %v", n.Name(), err))) + glog.Errorf(kwlog(fmt.Sprintf("unable to delete namespace %s. Error: %v", namespace, err))) } } func (n NamespaceCoreV1) Status(c KubeClient, namespace string) (interface{}, error) { - nsStatus, err := c.Client.CoreV1().Namespaces().Get(context.Background(), n.Name(), metav1.GetOptions{}) + nsStatus, err := c.Client.CoreV1().Namespaces().Get(context.Background(), namespace, metav1.GetOptions{}) if err != nil { return nil, fmt.Errorf(kwlog(fmt.Sprintf("Error getting namespace status: %v", err))) } @@ -927,7 +935,7 @@ func checkCRDInUse(crClient dynamic.NamespaceableResourceInterface, crdKind stri if err != nil && !errors.IsNotFound(err) && !strings.Contains(err.Error(), "not find") { glog.Errorf(kwlog(fmt.Sprintf("failed to list all CRs in other namespace error: %v, will keep this crd", err))) return true, err - } else { + } else if crsInOtherNS != nil { glog.V(5).Infof(kwlog(fmt.Sprintf("CRs in other namespace result: %v", crsInOtherNS))) items := crsInOtherNS.Items for _, item := range items { diff --git a/kube_operator/client.go b/kube_operator/client.go index 419ba82d3..e96b3f3ce 100644 --- a/kube_operator/client.go +++ b/kube_operator/client.go @@ -132,12 +132,14 @@ func (c KubeClient) Install(tar string, metadata map[string]interface{}, envVars // get and check namespace namespace := getFinalNamespace(reqNamespace, opNamespace) nodeNamespace := cutil.GetClusterNamespace() - if namespace != nodeNamespace && nodeNamespace != DEFAULT_ANAX_NAMESPACE { - return fmt.Errorf("Service failed to start for agreement %v. Could not deploy service into namespace %v because the agent's namespace is %v and it restricts all services to have the same namespace.", agId, namespace, nodeNamespace) + nodeIsNamespaceScope := cutil.IsNamespaceScoped() + if namespace != nodeNamespace && nodeIsNamespaceScope { + return fmt.Errorf("Service failed to start for agreement %v. Could not deploy service into namespace %v because the agent's namespace is namespace scoped, and it restricts all services to the agent namespace %v", agId, namespace, nodeNamespace) } // If the namespace was specified in the deployment then create the namespace object so it can be created - if _, ok := apiObjMap[K8S_NAMESPACE_TYPE]; !ok && namespace != nodeNamespace { + _, ok := apiObjMap[K8S_NAMESPACE_TYPE] + if !ok || namespace != opNamespace { nsObj := corev1.Namespace{TypeMeta: metav1.TypeMeta{Kind: "Namespace"}, ObjectMeta: metav1.ObjectMeta{Name: namespace}} apiObjMap[K8S_NAMESPACE_TYPE] = []APIObjectInterface{NamespaceCoreV1{NamespaceObject: &nsObj}} } @@ -180,6 +182,20 @@ func (c KubeClient) Uninstall(tar string, metadata map[string]interface{}, agId crd.Uninstall(c, namespace) } + // uninstall any remaining components of unknown type + for _, unknownObj := range apiObjMap[K8S_UNSTRUCTURED_TYPE] { + glog.Infof(kwlog(fmt.Sprintf("attempting to uninstall %v", unknownObj.Name()))) + unknownObj.Uninstall(c, namespace) + } + + nodeIsNamespaceScope := cutil.IsNamespaceScoped() + nodeNamespace := cutil.GetClusterNamespace() + // If the namespace was specified in the deployment then create the namespace object so it can be uninstalled + if _, ok := apiObjMap[K8S_NAMESPACE_TYPE]; !ok && namespace != nodeNamespace && !nodeIsNamespaceScope { + nsObj := corev1.Namespace{TypeMeta: metav1.TypeMeta{Kind: "Namespace"}, ObjectMeta: metav1.ObjectMeta{Name: namespace}} + apiObjMap[K8S_NAMESPACE_TYPE] = []APIObjectInterface{NamespaceCoreV1{NamespaceObject: &nsObj}} + } + baseK8sComponents := getBaseK8sKinds() // uninstall all the objects of built-in k8s types @@ -191,12 +207,6 @@ func (c KubeClient) Uninstall(tar string, metadata map[string]interface{}, agId } } - // uninstall any remaining components of unknown type - for _, unknownObj := range apiObjMap[K8S_UNSTRUCTURED_TYPE] { - glog.Infof(kwlog(fmt.Sprintf("attempting to uninstall %v", unknownObj.Name()))) - unknownObj.Uninstall(c, namespace) - } - glog.V(3).Infof(kwlog(fmt.Sprintf("Completed removal of all operator objects from the cluster."))) return nil } diff --git a/producer/producer_protocol_handler.go b/producer/producer_protocol_handler.go index 7e3900343..c093bdfd7 100644 --- a/producer/producer_protocol_handler.go +++ b/producer/producer_protocol_handler.go @@ -485,7 +485,7 @@ func (w *BaseProducerProtocolHandler) MatchClusterNamespace(tcPolicy *policy.Pol glog.Errorf(BPPHlogString(w.Name(), fmt.Sprintf("no cluster deployment configuration is provided."))) return false, nil } else { - compResult, _, reason := compcheck.CheckClusterNamespaceCompatibility(nodeType, cutil.GetClusterNamespace(), tcPolicy.ClusterNamespace, workload.ClusterDeployment, true, nil) + compResult, _, reason := compcheck.CheckClusterNamespaceCompatibility(nodeType, cutil.GetClusterNamespace(), cutil.IsNamespaceScoped(), tcPolicy.ClusterNamespace, workload.ClusterDeployment, "", true, nil) if compResult { glog.V(5).Infof(BPPHlogString(w.Name(), fmt.Sprintf("cluster namespace matches. %v", reason))) } else { diff --git a/test/docker/fs/etc/agent-in-kube/deployment.yaml.tmpl b/test/docker/fs/etc/agent-in-kube/deployment.yaml.tmpl index cc9760a75..e5327edcf 100644 --- a/test/docker/fs/etc/agent-in-kube/deployment.yaml.tmpl +++ b/test/docker/fs/etc/agent-in-kube/deployment.yaml.tmpl @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: agent - namespace: openhorizon-agent + namespace: agent-namespace spec: replicas: 1 selector: @@ -52,3 +52,5 @@ spec: value: "1" - name: ANAX_LOG_LEVEL value: "5" + - name: AGENT_NAMESPACE + value: "agent-namespace" diff --git a/test/docker/fs/etc/agent-in-kube/deployment_nocert.yaml.tmpl b/test/docker/fs/etc/agent-in-kube/deployment_nocert.yaml.tmpl index 65bf7fbd3..bb5b84c5b 100644 --- a/test/docker/fs/etc/agent-in-kube/deployment_nocert.yaml.tmpl +++ b/test/docker/fs/etc/agent-in-kube/deployment_nocert.yaml.tmpl @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: agent - namespace: openhorizon-agent + namespace: agent-namespace spec: replicas: 1 selector: @@ -47,3 +47,5 @@ spec: value: "1" - name: ANAX_LOG_LEVEL value: "5" + - name: AGENT_NAMESPACE + value: "agent-namespace" diff --git a/test/docker/fs/etc/agent-in-kube/persistent-claim.yaml b/test/docker/fs/etc/agent-in-kube/persistent-claim.yaml index 87cd5b1ed..e34f3a8ba 100644 --- a/test/docker/fs/etc/agent-in-kube/persistent-claim.yaml +++ b/test/docker/fs/etc/agent-in-kube/persistent-claim.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: name: agent-pvc-horizon - namespace: openhorizon-agent + namespace: agent-namespace spec: storageClassName: "microk8s-hostpath" accessModes: diff --git a/test/gov/run_kube.sh b/test/gov/run_kube.sh index 1c1b71ab8..415461022 100755 --- a/test/gov/run_kube.sh +++ b/test/gov/run_kube.sh @@ -12,7 +12,7 @@ ANAX_SOURCE=$2 EXCH_ROOTPW=$3 DOCKER_TEST_NETWORK=$4 -NAME_SPACE="openhorizon-agent" +NAME_SPACE="agent-namespace" CONFIGMAP_NAME="agent-configmap-horizon" SECRET_NAME="agent-secret-cert" PVC_NAME="agent-pvc-horizon" @@ -265,5 +265,6 @@ $cprefix microk8s.kubectl cp $PWD/gov/input_files/k8s_deploy/node.policy.json ${ $cprefix microk8s.kubectl cp $PWD/gov/input_files/k8s_deploy/node_ui.json ${NAME_SPACE}/${POD}:/home/agentuser/. $cprefix microk8s.kubectl exec ${POD} -it -n ${NAME_SPACE} -- env ARCH=${ARCH} /usr/bin/hzn register -f /home/agentuser/node_ui.json -p e2edev@somecomp.com/sk8s -u root/root:${EXCH_ROOTPW} +#$cprefix microk8s.kubectl exec ${POD} -it -n ${NAME_SPACE} -- env ARCH=${ARCH} /usr/bin/hzn register -f /home/agentuser/node_ui.json --policy /home/agentuser/node_ui.json -u root/root:${EXCH_ROOTPW} echo "Configured agent for policy, waiting for the agbot to start." diff --git a/test/gov/stop_kube.sh b/test/gov/stop_kube.sh index 2b503c796..2d7a576de 100755 --- a/test/gov/stop_kube.sh +++ b/test/gov/stop_kube.sh @@ -2,7 +2,7 @@ # set -x -NAME_SPACE="openhorizon-agent" +NAME_SPACE="agent-namespace" CONFIGMAP_NAME="agent-configmap-horizon" SECRET_NAME="agent-secret-cert"