Coverage Report

Created: 2024-12-19 04:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/build/source/nativelink-util/src/platform_properties.rs
Line
Count
Source
1
// Copyright 2024 The NativeLink Authors. All rights reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//    http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
use std::borrow::Cow;
16
use std::collections::HashMap;
17
18
use nativelink_metric::{
19
    publish, MetricFieldData, MetricKind, MetricPublishKnownKindData, MetricsComponent,
20
};
21
use nativelink_proto::build::bazel::remote::execution::v2::Platform as ProtoPlatform;
22
use serde::{Deserialize, Serialize};
23
24
/// `PlatformProperties` helps manage the configuration of platform properties to
25
/// keys and types. The scheduler uses these properties to decide what jobs
26
/// can be assigned to different workers. For example, if a job states it needs
27
/// a specific key, it will never be run on a worker that does not have at least
28
/// all the platform property keys configured on the worker.
29
///
30
/// Additional rules may be applied based on `PlatfromPropertyValue`.
31
#[derive(Eq, PartialEq, Clone, Debug, Default, Serialize, Deserialize, MetricsComponent)]
32
pub struct PlatformProperties {
33
    #[metric]
34
    pub properties: HashMap<String, PlatformPropertyValue>,
35
}
36
37
impl PlatformProperties {
38
    #[must_use]
39
43
    pub const fn new(map: HashMap<String, PlatformPropertyValue>) -> Self {
40
43
        Self { properties: map }
41
43
    }
42
43
    /// Determines if the worker's `PlatformProperties` is satisfied by this struct.
44
    #[must_use]
45
33
    pub fn is_satisfied_by(&self, worker_properties: &Self) -> bool {
46
37
        for (
property, check_value9
) in &self.properties {
47
9
            if let Some(worker_value) = worker_properties.properties.get(property) {
  Branch (47:20): [True: 9, False: 0]
  Branch (47:20): [Folded - Ignored]
48
9
                if !check_value.is_satisfied_by(worker_value) {
  Branch (48:20): [True: 5, False: 4]
  Branch (48:20): [Folded - Ignored]
49
5
                    return false;
50
4
                }
51
            } else {
52
0
                return false;
53
            }
54
        }
55
28
        true
56
33
    }
57
}
58
59
impl From<ProtoPlatform> for PlatformProperties {
60
0
    fn from(platform: ProtoPlatform) -> Self {
61
0
        let mut properties = HashMap::with_capacity(platform.properties.len());
62
0
        for property in platform.properties {
63
0
            properties.insert(
64
0
                property.name,
65
0
                PlatformPropertyValue::Unknown(property.value),
66
0
            );
67
0
        }
68
0
        Self { properties }
69
0
    }
70
}
71
72
/// Holds the associated value of the key and type.
73
///
74
/// Exact    - Means the worker must have this exact value.
75
/// Minimum  - Means that workers must have at least this number available. When
76
///            a worker executes a task that has this value, the worker will have
77
///            this value subtracted from the available resources of the worker.
78
/// Priority - Means the worker is given this information, but does not restrict
79
///            what workers can take this value. However, the worker must have the
80
///            associated key present to be matched.
81
///            TODO(allada) In the future this will be used by the scheduler and
82
///            worker to cause the scheduler to prefer certain workers over others,
83
///            but not restrict them based on these values.
84
#[derive(Eq, PartialEq, Hash, Clone, Ord, PartialOrd, Debug, Serialize, Deserialize)]
85
pub enum PlatformPropertyValue {
86
    Exact(String),
87
    Minimum(u64),
88
    Priority(String),
89
    Unknown(String),
90
}
91
92
impl PlatformPropertyValue {
93
    /// Same as `PlatformProperties::is_satisfied_by`, but on an individual value.
94
    #[must_use]
95
9
    pub fn is_satisfied_by(&self, worker_value: &Self) -> bool {
96
9
        if self == worker_value {
  Branch (96:12): [True: 4, False: 5]
  Branch (96:12): [Folded - Ignored]
97
4
            return true;
98
5
        }
99
5
        match self {
100
3
            Self::Minimum(v) => {
101
3
                if let Self::Minimum(worker_v) = worker_value {
  Branch (101:24): [True: 3, False: 0]
  Branch (101:24): [Folded - Ignored]
102
3
                    return worker_v >= v;
103
0
                }
104
0
                false
105
            }
106
            // Priority is used to pass info to the worker and not restrict which
107
            // workers can be selected, but might be used to prefer certain workers
108
            // over others.
109
0
            Self::Priority(_) => true,
110
            // Success exact case is handled above.
111
2
            Self::Exact(_) | Self::Unknown(_) => false,
112
        }
113
9
    }
114
115
2
    pub fn as_str(&self) -> Cow<str> {
116
2
        match self {
117
0
            Self::Exact(value) | Self::Priority(value) | Self::Unknown(value) => {
118
0
                Cow::Borrowed(value)
119
            }
120
2
            Self::Minimum(value) => Cow::Owned(value.to_string()),
121
        }
122
2
    }
123
}
124
125
impl MetricsComponent for PlatformPropertyValue {
126
0
    fn publish(
127
0
        &self,
128
0
        kind: MetricKind,
129
0
        field_metadata: MetricFieldData,
130
0
    ) -> Result<MetricPublishKnownKindData, nativelink_metric::Error> {
131
0
        let name = field_metadata.name.into_owned();
132
0
        let help = field_metadata.help.as_ref();
133
0
        match self {
134
0
            Self::Exact(v) => publish!(name, v, kind, help, "exact"),
135
0
            Self::Minimum(v) => publish!(name, v, kind, help, "minimum"),
136
0
            Self::Priority(v) => publish!(name, v, kind, help, "priority"),
137
0
            Self::Unknown(v) => publish!(name, v, kind, help, "unknown"),
138
        }
139
140
0
        Ok(MetricPublishKnownKindData::Component)
141
0
    }
142
}