This repository was archived by the owner on Oct 3, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 94
Add Resource API #193
Merged
mayurkale22
merged 6 commits into
census-instrumentation:master
from
mayurkale22:resourceAPI
Nov 27, 2018
Merged
Add Resource API #193
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
5539c10
Add Resource API
mayurkale22 017ebbe
Fix reviews
mayurkale22 cdbc33f
Add interface: Resource
mayurkale22 f3796a2
fix reviews
mayurkale22 bc169fe
remove null from Resource: type
mayurkale22 46df0d4
Add interface for Labels
mayurkale22 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| /** | ||
| * Copyright 2018 Google LLC | ||
| * | ||
| * 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. | ||
| */ | ||
|
|
||
| /** | ||
| * Internal utility methods for working with tag keys, tag values, and metric | ||
| * names. | ||
| */ | ||
| export class StringUtils { | ||
| /** | ||
| * Determines whether the String contains only printable characters. | ||
| * | ||
| * @param {string} str The String to be validated. | ||
| * @returns {boolean} Whether the String contains only printable characters. | ||
| */ | ||
| static isPrintableString(str: string): boolean { | ||
| for (let i = 0; i < str.length; i++) { | ||
| const ch: string = str.charAt(i); | ||
| if (!StringUtils.isPrintableChar(ch)) { | ||
| return false; | ||
| } | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| /** | ||
| * Determines whether the Character is printable. | ||
| * | ||
| * @param {string} str The Character to be validated. | ||
| * @returns {boolean} Whether the Character is printable. | ||
| */ | ||
| static isPrintableChar(ch: string): boolean { | ||
| return ch >= ' ' && ch <= '~'; | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,178 @@ | ||
| /** | ||
| * Copyright 2018, OpenCensus Authors | ||
| * | ||
| * 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. | ||
| */ | ||
|
|
||
| import {StringUtils} from '../internal/string-utils'; | ||
|
|
||
| import {Labels, Resource} from './types'; | ||
|
|
||
| /** | ||
| * Resource represents a resource, which capture identifying information about | ||
| * the entities for which signals (stats or traces) are reported. It further | ||
| * provides a framework for detection of resource information from the | ||
| * environment and progressive population as signals propagate from the core | ||
| * instrumentation library to a backend's exporter. | ||
| */ | ||
| export class CoreResource { | ||
| // Type, label keys, and label values should not exceed 256 characters. | ||
| private static readonly MAX_LENGTH = 255; | ||
|
isaikevych marked this conversation as resolved.
|
||
|
|
||
| // OC_RESOURCE_LABELS is a comma-separated list of labels. | ||
| private static readonly COMMA_SEPARATOR = ','; | ||
|
|
||
| // OC_RESOURCE_LABELS contains key value pair separated by '='. | ||
| private static readonly LABEL_KEY_VALUE_SPLITTER = '='; | ||
|
|
||
| private static readonly ENV_TYPE = | ||
| CoreResource.parseResourceType(process.env.OC_RESOURCE_TYPE); | ||
| private static readonly ENV_LABEL_MAP = | ||
| CoreResource.parseResourceLabels(process.env.OC_RESOURCE_LABELS); | ||
| private static readonly ERROR_MESSAGE_INVALID_CHARS = | ||
| 'should be a ASCII string with a length greater than 0 and not exceed ' + | ||
| CoreResource.MAX_LENGTH + ' characters.'; | ||
| private static readonly ERROR_MESSAGE_INVALID_VALUE = | ||
| 'should be a ASCII string with a length not exceed ' + | ||
| CoreResource.MAX_LENGTH + ' characters.'; | ||
|
|
||
| /** | ||
| * Returns a Resource. This resource information is loaded from the | ||
| * OC_RESOURCE_TYPE and OC_RESOURCE_LABELS environment variables. | ||
| * | ||
| * @returns {Resource} The resource. | ||
| */ | ||
| static createFromEnvironmentVariables(): Resource { | ||
| return {type: CoreResource.ENV_TYPE, labels: CoreResource.ENV_LABEL_MAP}; | ||
| } | ||
|
|
||
| /** | ||
| * Returns a Resource that runs all input resources sequentially and merges | ||
| * their results. In case a type of label key is already set, the first set | ||
| * value takes precedence. | ||
| * | ||
| * @param {Resource[]} resources The list of the resources. | ||
| * @returns {Resource} The resource. | ||
| */ | ||
| static mergeResources(resources: Resource[]): Resource { | ||
| let currentResource: Resource; | ||
| for (const resource of resources) { | ||
| currentResource = this.merge(currentResource, resource); | ||
| } | ||
| return currentResource; | ||
| } | ||
|
|
||
| /** | ||
| * Creates a resource type from the OC_RESOURCE_TYPE environment variable. | ||
| * | ||
| * OC_RESOURCE_TYPE: A string that describes the type of the resource | ||
| * prefixed by a domain namespace, e.g. “kubernetes.io/container”. | ||
|
isaikevych marked this conversation as resolved.
|
||
| * | ||
| * @param {string} rawEnvType The resource type. | ||
| * @returns {string} The sanitized resource type. | ||
| */ | ||
| private static parseResourceType(rawEnvType: string): string { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you please move private methods to top for more readability? |
||
| if (rawEnvType) { | ||
| if (!CoreResource.isValidAndNotEmpty(rawEnvType)) { | ||
| throw new Error(`Type ${CoreResource.ERROR_MESSAGE_INVALID_CHARS}`); | ||
| } | ||
| return rawEnvType.trim(); | ||
| } | ||
| return null; | ||
| } | ||
|
|
||
| /** | ||
| * Creates a label map from the OC_RESOURCE_LABELS environment variable. | ||
| * | ||
| * OC_RESOURCE_LABELS: A comma-separated list of labels describing the | ||
| * source in more detail, e.g. “key1=val1,key2=val2”. Domain names and paths | ||
| * are accepted as label keys. Values may be quoted or unquoted in general. If | ||
| * a value contains whitespaces, =, or " characters, it must always be quoted. | ||
|
isaikevych marked this conversation as resolved.
|
||
| * | ||
| * @param {string} rawEnvLabels The resource labels as a comma-seperated list | ||
| * of key/value pairs. | ||
| * @returns {Labels} The sanitized resource labels. | ||
| */ | ||
| private static parseResourceLabels(rawEnvLabels: string): Labels { | ||
| const labels: Labels = {}; | ||
| if (rawEnvLabels) { | ||
| const rawLabels: string[] = rawEnvLabels.split(this.COMMA_SEPARATOR, -1); | ||
| for (const rawLabel of rawLabels) { | ||
| const keyValuePair: string[] = | ||
| rawLabel.split(this.LABEL_KEY_VALUE_SPLITTER, -1); | ||
| if (keyValuePair.length !== 2) { | ||
| continue; | ||
| } | ||
| let [key, value] = keyValuePair; | ||
| // Leading and trailing whitespaces are trimmed. | ||
| key = key.trim(); | ||
| value = value.trim().split('^"|"$').join(''); | ||
| if (!CoreResource.isValidAndNotEmpty(key)) { | ||
| throw new Error( | ||
| `Label key ${CoreResource.ERROR_MESSAGE_INVALID_CHARS}`); | ||
| } | ||
| if (!CoreResource.isValid(value)) { | ||
| throw new Error( | ||
| `Label value ${CoreResource.ERROR_MESSAGE_INVALID_VALUE}`); | ||
| } | ||
| labels[key] = value; | ||
| } | ||
| } | ||
| return labels; | ||
| } | ||
|
|
||
| /** | ||
| * Returns a new, merged Resource by merging two resources. In case of | ||
| * a collision, first resource takes precedence. | ||
|
isaikevych marked this conversation as resolved.
|
||
| * | ||
| * @param {Resource} resource The resource object. | ||
| * @param {Resource} otherResource The resource object. | ||
| * @returns {Resource} A new, merged Resource. | ||
| */ | ||
| private static merge(resource: Resource, otherResource: Resource): Resource { | ||
| if (!resource) { | ||
| return otherResource; | ||
| } | ||
| if (!otherResource) { | ||
| return resource; | ||
| } | ||
| return { | ||
| type: resource.type || otherResource.type, | ||
| labels: Object.assign({}, otherResource.labels, resource.labels) | ||
| }; | ||
| } | ||
|
|
||
|
|
||
| /** | ||
| * Determines whether the given String is a valid printable ASCII string with | ||
| * a length not exceed MAX_LENGTH characters. | ||
|
isaikevych marked this conversation as resolved.
|
||
| * | ||
| * @param {string} str The String to be validated. | ||
| * @returns {boolean} Whether the String is valid. | ||
| */ | ||
| private static isValid(name: string): boolean { | ||
| return name.length <= CoreResource.MAX_LENGTH && | ||
| StringUtils.isPrintableString(name); | ||
| } | ||
|
|
||
| /** | ||
| * Determines whether the given String is a valid printable ASCII string with | ||
| * a length greater than 0 and not exceed MAX_LENGTH characters. | ||
|
isaikevych marked this conversation as resolved.
|
||
| * | ||
| * @param {string} str The String to be validated. | ||
| * @returns {boolean} Whether the String is valid and not empty. | ||
| */ | ||
| private static isValidAndNotEmpty(name: string): boolean { | ||
| return name && name.length > 0 && CoreResource.isValid(name); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| /** | ||
| * Copyright 2018, OpenCensus Authors | ||
| * | ||
| * 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. | ||
| */ | ||
|
|
||
| /** A Resource describes the entity for which a signal was collected. */ | ||
| export interface Resource { | ||
| /** | ||
| * An optional string which describes a well-known type of resource. | ||
| */ | ||
| readonly type: string; | ||
|
|
||
| /** | ||
| * A dictionary of labels with string keys and values that provide information | ||
| * about the entity. | ||
| */ | ||
| readonly labels: Labels; | ||
| } | ||
|
|
||
| /** Labels are maps of keys -> values */ | ||
| export interface Labels { [key: string]: string; } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,104 @@ | ||
| /** | ||
| * Copyright 2018, OpenCensus Authors | ||
| * | ||
| * 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. | ||
| */ | ||
|
|
||
| import * as assert from 'assert'; | ||
|
|
||
| process.env.OC_RESOURCE_TYPE = 'k8s.io/container'; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My guess that its necessary just for first |
||
| process.env.OC_RESOURCE_LABELS = | ||
| 'k8s.io/pod/name="pod-xyz-123",k8s.io/container/name="c1",k8s.io/namespace/name="default"'; | ||
|
|
||
| import {CoreResource} from '../src/resource/resource'; | ||
| import {Resource, Labels} from '../src/resource/types'; | ||
|
|
||
| describe('Resource()', () => { | ||
| after(() => { | ||
| delete process.env.OC_RESOURCE_TYPE; | ||
| delete process.env.OC_RESOURCE_LABELS; | ||
| }); | ||
|
|
||
| it('should return resource information from environment variables', () => { | ||
| const resource = CoreResource.createFromEnvironmentVariables(); | ||
| const actualLabels = resource.labels; | ||
| const expectedLabels: Labels = { | ||
| 'k8s.io/container/name': '"c1"', | ||
| 'k8s.io/namespace/name': '"default"', | ||
| 'k8s.io/pod/name': '"pod-xyz-123"' | ||
| }; | ||
|
|
||
| assert.strictEqual(resource.type, 'k8s.io/container'); | ||
| assert.equal(Object.keys(actualLabels).length, 3); | ||
| assert.deepEqual(actualLabels, expectedLabels); | ||
| }); | ||
| }); | ||
|
|
||
| describe('mergeResources()', () => { | ||
| const DEFAULT_RESOURCE: Resource = {type: null, labels: {}}; | ||
| const DEFAULT_RESOURCE_1: Resource = {type: 'default', labels: {'a': '100'}}; | ||
| const RESOURCE_1: Resource = {type: 't1', labels: {'a': '1', 'b': '2'}}; | ||
| const RESOURCE_2: | ||
| Resource = {type: 't2', labels: {'a': '1', 'b': '3', 'c': '4'}}; | ||
|
|
||
| it('merge resources with default, resource1', () => { | ||
| const resources: Resource[] = [DEFAULT_RESOURCE, RESOURCE_1]; | ||
| const resource = CoreResource.mergeResources(resources); | ||
| const expectedLabels: Labels = {'a': '1', 'b': '2'}; | ||
|
|
||
| assert.equal(resource.type, 't1'); | ||
| assert.equal(Object.keys(resource.labels).length, 2); | ||
| assert.deepEqual(resource.labels, expectedLabels); | ||
| }); | ||
|
|
||
| it('merge resources with default, resource1, resource2 = null', () => { | ||
|
isaikevych marked this conversation as resolved.
|
||
| const resources: Resource[] = [DEFAULT_RESOURCE, RESOURCE_1, null]; | ||
| const resource = CoreResource.mergeResources(resources); | ||
| const expectedLabels: Labels = {'a': '1', 'b': '2'}; | ||
|
|
||
| assert.equal(resource.type, 't1'); | ||
| assert.equal(Object.keys(resource.labels).length, 2); | ||
| assert.deepEqual(resource.labels, expectedLabels); | ||
| }); | ||
|
|
||
| it('merge resources with default, resource1 = null, resource2', () => { | ||
| const resources: Resource[] = [DEFAULT_RESOURCE, null, RESOURCE_2]; | ||
| const resource = CoreResource.mergeResources(resources); | ||
| const expectedLabels: Labels = {'a': '1', 'b': '3', 'c': '4'}; | ||
|
|
||
| assert.equal(resource.type, 't2'); | ||
| assert.equal(Object.keys(resource.labels).length, 3); | ||
| assert.deepEqual(resource.labels, expectedLabels); | ||
| }); | ||
|
|
||
| it('merge resources with default1, resource1, resource2', () => { | ||
| const resources: Resource[] = [DEFAULT_RESOURCE_1, RESOURCE_1, RESOURCE_2]; | ||
| const resource = CoreResource.mergeResources(resources); | ||
| const expectedLabels: Labels = {'a': '100', 'b': '2', 'c': '4'}; | ||
|
|
||
| assert.equal(resource.type, 'default'); | ||
| assert.equal(Object.keys(resource.labels).length, 3); | ||
| assert.deepEqual(resource.labels, expectedLabels); | ||
| }); | ||
|
|
||
| it('merge resources with default, resource1 = undefined, resource2 = undefined', | ||
| () => { | ||
| const resources: Resource[] = [DEFAULT_RESOURCE_1, undefined, undefined]; | ||
| const resource = CoreResource.mergeResources(resources); | ||
| const expectedLabels: Labels = {'a': '100'}; | ||
|
|
||
| assert.equal(resource.type, 'default'); | ||
| assert.equal(Object.keys(resource.labels).length, 1); | ||
| assert.deepEqual(resource.labels, expectedLabels); | ||
| }); | ||
| }); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| /** | ||
| * Copyright 2018, OpenCensus Authors | ||
| * | ||
| * 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. | ||
| */ | ||
|
|
||
| import * as assert from 'assert'; | ||
| import {StringUtils} from '../src/internal/string-utils'; | ||
|
|
||
| describe('StringUtils', () => { | ||
| it('should return true when string is printable', () => { | ||
| const isValid = StringUtils.isPrintableString('abcd'); | ||
| assert.deepStrictEqual(isValid, true); | ||
| }); | ||
|
|
||
| it('should return false when string is not printable', () => { | ||
| const isValid = StringUtils.isPrintableString('\x00-\xFF'); | ||
| assert.deepStrictEqual(isValid, false); | ||
| }); | ||
| }); |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.