summaryrefslogtreecommitdiff
path: root/matcher/validmatcher.go
blob: c89abf1bc86c7289eb464cabec50523a6d594e4a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/*
 *  Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License
 */

// File matcher/validmatcher.go provides ValidMatcher structure which implements
// Matcher interface. It should be used for handling events caused by validation
// of requests after ValidAfter time is passed.

package matcher

import (
	"time"

	. "git.tizen.org/tools/boruta"
)

// ValidMatcher implements Matcher interface for handling requests validation.
type ValidMatcher struct {
	Matcher
	// requests provides internal boruta access to requests.
	requests RequestsManager
	// workers provides internal boruta access to workers.
	workers WorkersManager
	// jobs provides internal boruta access to jobs.
	jobs JobsManager
}

// NewValidMatcher creates a new ValidMatcher structure.
func NewValidMatcher(r RequestsManager, w WorkersManager, j JobsManager) *ValidMatcher {
	return &ValidMatcher{
		requests: r,
		workers:  w,
		jobs:     j,
	}
}

// Notify implements Matcher interface. This method reacts on events passed to
// matcher. In this implementation requests' IDs are ignored as requests must be
// matched in order they are placed in requests priority queue.
func (m ValidMatcher) Notify([]ReqID) {
	// Repeat verification until iterateRequests() returns false indicating that
	// there is no more job to be done.
	for m.iterateRequests() {
	}
}

// iterateRequests visits all requests in order they are placed in requests
// priority queue, verifies if they can be run and tries to match an idle worker.
// Method returns true if iteration should be repeated or false if there is
// nothing more to be done.
func (m ValidMatcher) iterateRequests() bool {

	err := m.requests.InitIteration()
	if err != nil {
		// TODO log critical logic error. InitIterations should return no error
		// as no iterations should by run by any other goroutine.
		panic("Critical logic error. No iterations over requests collection should be running.")
	}
	defer m.requests.TerminateIteration()

	now := time.Now()
	// Iterate on requests priority queue.
	for rid, rok := m.requests.Next(); rok; rid, rok = m.requests.Next() {
		// Verify if request is ready to be run.
		if !m.requests.VerifyIfReady(rid, now) {
			continue
		}
		// Request is ready to be run. Get full information about it.
		req, err := m.requests.Get(rid)
		if err != nil {
			continue
		}

		// Try finding an idle worker matching requests requirements.
		if m.matchWorkers(req) {
			// A match was made. Restarting iterations to process other requests.
			return true
		}
	}
	// All requests have been analyzed. No repetition is required.
	return false
}

// matchWorkers tries to find the best of the idle workers matching capabilities
// and groups of the requests. Best worker is the one with least matching penalty.
// If such worker is found a job is created and the request is processed.
func (m ValidMatcher) matchWorkers(req ReqInfo) bool {

	worker, err := m.workers.TakeBestMatchingWorker(req.Owner.Groups, req.Caps)
	if err != nil {
		// No matching worker was found.
		return false
	}
	// Match found.
	err = m.jobs.Create(req.ID, worker)
	if err != nil {
		// TODO log error.
		goto fail
	}
	err = m.requests.Run(req.ID, worker)
	if err != nil {
		// TODO log error.
		goto fail
	}
	return true

fail:
	// Creating job failed. Bringing worker back to IDLE state.
	m.workers.PrepareWorker(worker, false)
	return false
}