Skip to content

Commit d670d6c

Browse files
authored
Merge branch 'main' into inc_perf
2 parents 94be4e5 + d2e3fd0 commit d670d6c

File tree

19 files changed

+1924
-57
lines changed

19 files changed

+1924
-57
lines changed

doc/build/build.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ $
3434
3535
### Additional software needed
3636
37-
To build the controller and to run the end to end tests locally you will need to have the following software installed:
37+
To build the controller and run the end-to-end tests locally, you will need to have the following software installed:
3838
3939
* `Go` (version 1.19)
4040
* `kind` (version 0.18)
@@ -48,7 +48,7 @@ On MacOS you will need to have `readlink` executable installed (`brew install co
4848
4949
### Build the Executable
5050
51-
From the root directory of the repository, you may build only the executable, or you can build the image directly.
51+
From the repository's root directory, you may build only the executable, or you can build the image directly.
5252
5353
To to build the executable, execute:
5454
@@ -61,7 +61,7 @@ Compiling controller
6161
CGO_ENABLED=0 go build -o _output/bin/mcad-controller ./cmd/kar-controllers/
6262
```
6363
64-
Ensure the executable `mcad-controllers` are created in the target output directory:
64+
Ensure the executable `mcad-controller` is created in the target output directory:
6565
6666
```bash
6767
multi-cluster-app-dispatcher $ ls _output/bin

pkg/controller/queuejob/queuejob_controller_ex.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ func (qjm *XController) PreemptQueueJobs() {
309309
}
310310
// cannot use cleanup AW, since it puts AW back in running state
311311
//TODO: We need to remove the use of AddUnschedulableIfNotPresent
312-
go qjm.qjqueue.AddUnschedulableIfNotPresent(updateNewJob)
312+
qjm.qjqueue.AddUnschedulableIfNotPresent(updateNewJob)
313313

314314
// Move to next AW
315315
continue

pkg/quotaplugins/quota-forest/quota-manager/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,12 @@ A summary of the API interface to the Quota Manager follows.
188188
- Forest Updates
189189
- refresh (effect) updates from caches
190190
- `UpdateForest(forestName)`
191+
- Undo consumer allocation: Two calls are provided to try to allocate a consumer, and if unaccepted, to undo the effect of the allocation trial. If the trial is accepted, no further action is needed. Otherwise, the undo has to be called right after the try allocation, without making any calls to change the trees or allocate/deallocate consumers. These operations are intended only during Normal mode.
192+
(Note: This design pattern puts the burden on the caller of the quota manager to make sure that TryAllocate() and UndoAllocate() are run in an atomic fashion. So, a lock is needed for that purpose. An example is provided in the TestQuotaManagerParallelTryUndoAllocation() in [quotamanagerundo_test.go](quota/quotamanagerundo_test.go))
193+
- `TryAllocate(treeName, consumerID)`
194+
- `UndoAllocate(treeName, consumerID)`
195+
- `TryAllocateForest(forestName, consumerID)`
196+
- `UndoAllocateForest(forestName, consumerID)`
191197

192198
Examples of using the Quota Manager in the case of a [single tree](demos/manager/tree/demo.go) and a [forest](demos/manager/forest/demo.go) are provided.
193199

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*
2+
Copyright 2023 The Multi-Cluster App Dispatcher Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
package main
17+
18+
import (
19+
"flag"
20+
"fmt"
21+
"os"
22+
23+
"github.com/project-codeflare/multi-cluster-app-dispatcher/pkg/quotaplugins/quota-forest/quota-manager/quota"
24+
"k8s.io/klog/v2"
25+
)
26+
27+
func main() {
28+
klog.InitFlags(nil)
29+
flag.Set("v", "4")
30+
flag.Set("skip_headers", "true")
31+
klog.SetOutput(os.Stdout)
32+
flag.Parse()
33+
defer klog.Flush()
34+
35+
fmt.Println("Demo of allocation and de-allocation of consumers on a forest using the quota manager.")
36+
fmt.Println()
37+
prefix := "../../../samples/forest/"
38+
indent := "===> "
39+
forestName := "Context-Service"
40+
treeNames := []string{"ContextTree", "ServiceTree"}
41+
42+
// create a quota manager
43+
fmt.Println(indent + "Creating quota manager ... " + "\n")
44+
quotaManager := quota.NewManager()
45+
quotaManager.SetMode(quota.Normal)
46+
fmt.Println(quotaManager.GetModeString())
47+
fmt.Println()
48+
49+
// create multiple trees
50+
fmt.Println(indent + "Creating multiple trees ..." + "\n")
51+
for _, treeName := range treeNames {
52+
fName := prefix + treeName + ".json"
53+
fmt.Printf("Tree file name: %s\n", fName)
54+
jsonTree, err := os.ReadFile(fName)
55+
if err != nil {
56+
fmt.Printf("error reading quota tree file: %s", fName)
57+
return
58+
}
59+
_, err = quotaManager.AddTreeFromString(string(jsonTree))
60+
if err != nil {
61+
fmt.Printf("error adding tree %s: %v", treeName, err)
62+
return
63+
}
64+
}
65+
66+
// create forest
67+
fmt.Println(indent + "Creating forest " + forestName + " ..." + "\n")
68+
quotaManager.AddForest(forestName)
69+
for _, treeName := range treeNames {
70+
quotaManager.AddTreeToForest(forestName, treeName)
71+
}
72+
fmt.Println(quotaManager)
73+
74+
// create consumer jobs
75+
fmt.Println(indent + "Allocating consumers on forest ..." + "\n")
76+
jobs := []string{"job1", "job2", "job3", "job4", "job5"}
77+
for _, job := range jobs {
78+
79+
// create consumer info
80+
fName := prefix + job + ".json"
81+
fmt.Printf("Consumer file name: %s\n", fName)
82+
consumerInfo, err := quota.NewConsumerInfoFromFile(fName)
83+
if err != nil {
84+
fmt.Printf("error reading consumer file: %s \n", fName)
85+
continue
86+
}
87+
consumerID := consumerInfo.GetID()
88+
89+
// add consumer info to quota manager
90+
quotaManager.AddConsumer(consumerInfo)
91+
92+
// allocate forest consumer instance of the consumer info
93+
if job == "job4" {
94+
_, err = quotaManager.TryAllocateForest(forestName, consumerID)
95+
if err != nil {
96+
fmt.Printf("error allocating consumer: %v \n", err)
97+
}
98+
err = quotaManager.UndoAllocateForest(forestName, "job-4")
99+
if err != nil {
100+
fmt.Printf("error undoing allocation consumer: %v \n", err)
101+
}
102+
_, err = quotaManager.AllocateForest(forestName, consumerID)
103+
} else {
104+
_, err = quotaManager.AllocateForest(forestName, consumerID)
105+
}
106+
107+
if err != nil {
108+
fmt.Printf("error allocating consumer: %v \n", err)
109+
quotaManager.RemoveConsumer((consumerID))
110+
continue
111+
}
112+
}
113+
114+
// de-allocate consumers from forest
115+
fmt.Println(indent + "De-allocating consumers from forest ..." + "\n")
116+
for _, id := range quotaManager.GetAllConsumerIDs() {
117+
quotaManager.DeAllocateForest(forestName, id)
118+
quotaManager.RemoveConsumer(id)
119+
}
120+
fmt.Println()
121+
fmt.Println(quotaManager)
122+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/*
2+
Copyright 2023 The Multi-Cluster App Dispatcher Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
package main
17+
18+
import (
19+
"flag"
20+
"fmt"
21+
"os"
22+
23+
"github.com/project-codeflare/multi-cluster-app-dispatcher/pkg/quotaplugins/quota-forest/quota-manager/quota"
24+
"github.com/project-codeflare/multi-cluster-app-dispatcher/pkg/quotaplugins/quota-forest/quota-manager/quota/core"
25+
klog "k8s.io/klog/v2"
26+
)
27+
28+
func main() {
29+
klog.InitFlags(nil)
30+
flag.Set("v", "4")
31+
flag.Set("skip_headers", "true")
32+
klog.SetOutput(os.Stdout)
33+
flag.Parse()
34+
defer klog.Flush()
35+
36+
prefix := "../../../samples/tree/"
37+
treeFileName := prefix + "tree.json"
38+
caFileName := prefix + "ca.json"
39+
cbFileName := prefix + "cb.json"
40+
ccFileName := prefix + "cc.json"
41+
cdFileName := prefix + "cd.json"
42+
ceFileName := prefix + "ce.json"
43+
44+
// create a quota manager
45+
fmt.Println("==> Creating Quota Manager")
46+
fmt.Println("**************************")
47+
quotaManager := quota.NewManager()
48+
treeJsonString, err := os.ReadFile(treeFileName)
49+
if err != nil {
50+
fmt.Printf("error reading quota tree file: %s", treeFileName)
51+
return
52+
}
53+
quotaManager.SetMode(quota.Normal)
54+
55+
// add a quota tree from file
56+
treeName, err := quotaManager.AddTreeFromString(string(treeJsonString))
57+
if err != nil {
58+
fmt.Printf("error adding tree %s: %v", treeName, err)
59+
return
60+
}
61+
62+
// allocate consumers
63+
allocate(quotaManager, treeName, caFileName, false)
64+
allocate(quotaManager, treeName, cbFileName, false)
65+
allocate(quotaManager, treeName, ccFileName, false)
66+
67+
// try and undo allocation
68+
allocate(quotaManager, treeName, cdFileName, true)
69+
undoAllocate(quotaManager, treeName, cdFileName)
70+
71+
// allocate consumers
72+
allocate(quotaManager, treeName, ceFileName, false)
73+
}
74+
75+
// allocate consumer from file
76+
func allocate(quotaManager *quota.Manager, treeName string, consumerFileName string, try bool) {
77+
consumerInfo := getConsumerInfo(consumerFileName)
78+
if consumerInfo == nil {
79+
fmt.Printf("error reading consumer file: %s", consumerFileName)
80+
return
81+
}
82+
consumerID := consumerInfo.GetID()
83+
fmt.Println("==> Allocating consumer " + consumerID)
84+
fmt.Println("**************************")
85+
quotaManager.AddConsumer(consumerInfo)
86+
87+
var allocResponse *core.AllocationResponse
88+
var err error
89+
if try {
90+
allocResponse, err = quotaManager.TryAllocate(treeName, consumerID)
91+
} else {
92+
allocResponse, err = quotaManager.Allocate(treeName, consumerID)
93+
}
94+
if err != nil {
95+
fmt.Printf("error allocating consumer: %v", err)
96+
return
97+
}
98+
fmt.Println(allocResponse)
99+
fmt.Println(quotaManager)
100+
}
101+
102+
// undo most recent consumer allocation
103+
func undoAllocate(quotaManager *quota.Manager, treeName string, consumerFileName string) {
104+
consumerInfo := getConsumerInfo(consumerFileName)
105+
if consumerInfo == nil {
106+
fmt.Printf("error reading consumer file: %s", consumerFileName)
107+
return
108+
}
109+
consumerID := consumerInfo.GetID()
110+
fmt.Println("==> Undo allocating consumer " + consumerID)
111+
fmt.Println("**************************")
112+
quotaManager.UndoAllocate(treeName, consumerID)
113+
fmt.Println(quotaManager)
114+
}
115+
116+
// get consumer info from yaml file
117+
func getConsumerInfo(consumerFileName string) *quota.ConsumerInfo {
118+
consumerInfo, err := quota.NewConsumerInfoFromFile(consumerFileName)
119+
if err != nil {
120+
fmt.Printf("error reading consumer file: %s", consumerFileName)
121+
return nil
122+
}
123+
return consumerInfo
124+
}

pkg/quotaplugins/quota-forest/quota-manager/quota/consumerinfo.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright 2022 The Multi-Cluster App Dispatcher Authors.
2+
Copyright 2022, 2023 The Multi-Cluster App Dispatcher Authors.
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -19,7 +19,7 @@ package quota
1919
import (
2020
"encoding/json"
2121
"fmt"
22-
"io/ioutil"
22+
"os"
2323

2424
"github.com/project-codeflare/multi-cluster-app-dispatcher/pkg/quotaplugins/quota-forest/quota-manager/quota/core"
2525
"github.com/project-codeflare/multi-cluster-app-dispatcher/pkg/quotaplugins/quota-forest/quota-manager/quota/utils"
@@ -49,7 +49,7 @@ func NewConsumerInfo(consumerStruct utils.JConsumer) (*ConsumerInfo, error) {
4949

5050
// NewConsumerInfoFromFile : create a new ConsumerInfo from Json file
5151
func NewConsumerInfoFromFile(consumerFileName string) (*ConsumerInfo, error) {
52-
byteValue, err := ioutil.ReadFile(consumerFileName)
52+
byteValue, err := os.ReadFile(consumerFileName)
5353
if err != nil {
5454
return nil, err
5555
}

pkg/quotaplugins/quota-forest/quota-manager/quota/core/consumer.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
/*
2-
Copyright 2022 The Multi-Cluster App Dispatcher Authors.
2+
Copyright 2022, 2023 The Multi-Cluster App Dispatcher Authors.
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
66
You may obtain a copy of the License at
77
8-
http://www.apache.org/licenses/LICENSE-2.0
8+
http://www.apache.org/licenses/LICENSE-2.0
99
1010
Unless required by applicable law or agreed to in writing, software
1111
distributed under the License is distributed on an "AS IS" BASIS,
@@ -119,6 +119,13 @@ func (c *Consumer) IsAllocated() bool {
119119
return c.aNode != nil
120120
}
121121

122+
// ByID implements sort.Interface based on the ID field.
123+
type ByID []*Consumer
124+
125+
func (c ByID) Len() int { return len(c) }
126+
func (c ByID) Less(i, j int) bool { return c[i].id < c[j].id }
127+
func (c ByID) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
128+
122129
// String : a print out of the consumer
123130
func (c *Consumer) String() string {
124131
var b bytes.Buffer

pkg/quotaplugins/quota-forest/quota-manager/quota/core/forestconsumer.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
/*
2-
Copyright 2022 The Multi-Cluster App Dispatcher Authors.
2+
Copyright 2022, 2023 The Multi-Cluster App Dispatcher Authors.
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
66
You may obtain a copy of the License at
77
8-
http://www.apache.org/licenses/LICENSE-2.0
8+
http://www.apache.org/licenses/LICENSE-2.0
99
1010
Unless required by applicable law or agreed to in writing, software
1111
distributed under the License is distributed on an "AS IS" BASIS,
@@ -19,7 +19,7 @@ import (
1919
"bytes"
2020
"encoding/json"
2121
"fmt"
22-
"io/ioutil"
22+
"os"
2323

2424
utils "github.com/project-codeflare/multi-cluster-app-dispatcher/pkg/quotaplugins/quota-forest/quota-manager/quota/utils"
2525
)
@@ -42,7 +42,7 @@ func NewForestConsumer(id string, consumers map[string]*Consumer) *ForestConsume
4242

4343
// NewForestConsumerFromFile : create a forest consumer from JSON file
4444
func NewForestConsumerFromFile(consumerFileName string, resourceNames map[string][]string) (*ForestConsumer, error) {
45-
byteValue, err := ioutil.ReadFile(consumerFileName)
45+
byteValue, err := os.ReadFile(consumerFileName)
4646
if err != nil {
4747
return nil, err
4848
}
@@ -108,7 +108,7 @@ func (fc *ForestConsumer) GetTreeConsumer(treeName string) *Consumer {
108108
return fc.consumers[treeName]
109109
}
110110

111-
//IsAllocated : is consumer allocated on all trees in the forest
111+
// IsAllocated : is consumer allocated on all trees in the forest
112112
func (fc *ForestConsumer) IsAllocated() bool {
113113
for _, consumer := range fc.consumers {
114114
if !consumer.IsAllocated() {

0 commit comments

Comments
 (0)