# Overview
[![Build Status](https://semaphoreci.com/api/v1/projects/353c0cf1-7dc6-4904-8045-ccfd0bb9331c/2423922/shields_badge.svg)](https://semaphoreci.com/cloudzero/cloudzero-identifiers)
[![License](https://img.shields.io/badge/License-UNLICENSED-blue.svg?style=flat)](#)

The **cloudzero-identifiers** project contains definitions and APIs for managing normalized [resource](docs/czrn.md), [relationship](docs/czrel.md), and [statistic](docs/statistic-id.md) identifiers. These identifiers represent strong domain concepts and are encountered across nearly every CloudZero system and feature including Reactor, Billing, and the System Map.  These identifiers also have well-defined and strict specifications.

For best results, please read the [Domain Glossary](https://cloudzero.atlassian.net/wiki/spaces/ENG/pages/354189365/Domain+Glossary) for an introduction into some of the core concepts in the CloudZero Domain.

# General Guidelines

## When to Use this Library
If you are building new features, odds are you will need to use some part of this library.  The Reactor's job in the CloudZero ecosystem is to translate and normalize the domains of cloud providers like AWS so that we can make magic happen downstream in Core.  Hence, the Reactor features are often responsible for creating identifiers using this library.  Features in Core like Billing and Graph7 most often use this library to validate, store, and analyze the identified entities.

## When to Change this Library
So you think you need to modify this library?  Here are some valid reasons to do so:
* AWS launches a new service that we've not seen before and we need to give identifiers to its resources
* You've noticed an ARN in the logs that we're not properly parsing
* A new resource type is discovered in the wild that we're not processing
* Fixing a bug in an identifier.  Identifiers are supposed to be immutable under ideal circumstances, but an immutable ID that's wrong is just as bad.  Consider it an act of swapping two immutable values; phasing out the old broken one and using the new correct one.  This is something that should be done carefully.
* We need to create a new CloudZero-specific resource to better model the cloud.
* Something needs to be updated in the documentation
* Something performs poorly in production and we have a better idea (be prepared to prove this with benchmarks!)
* A new function on an identifier is necessary (emphasis on the *necessary*).  General use across multiple projects should be demonstrated first.
* A new type of global identifier is introduced.  This should not happen often.

Breaking changes to the API are generally frowned upon.  Instead, version the internal API and give consumers the option to use the new one - but maintain the old one.

# Getting Started

This library is written in python and is designed to be installed from github labels using `pip`.  Consumers will add a line like this to their `requirements.txt` file: `git+ssh://git@github.com/Cloudzero/cloudzero-identifiers.git@1.0.5`

## Environment Prerequisites

1. MacOS 10 or your favorite Linux distro
1. Python 3.6+ with `virtualenv` or a similar tool
1. A github account with a configured SSH key

## Configuring your Development Environment
Note: The following instructions assume you are running MacOS:

1. Clone the repository

    ```bash
    $ git clone https://github.com/Cloudzero/cloudzero-identifiers
    ```

1. Create a Python Virtual Env

    It is strongly recommended that you configure a python virtual environment for the project.  Virtualenv is shown below, but you may use any tool of your choice:
    ```bash
    $ sudo pip3 install virtualenv
    $ virtualenv .cloudzero-identifiers -p python3
    $ source .cloudzero-identifiers/bin/activate
    ```

1. Install System Dependencies

    ```bash
    $ make init           # pip install ...
    ```

## Running Tests
When it comes to this library (or any library really), we live and die by having quality unit tests.  This code is run millions if not billions of times per day and needs to be rock-solid.  Code coverage should be close to 100% and all new PRs are expected to include covering unit tests.  Thankfully, testing these functions is pretty simple once you have some real world-examples to work with.  Please also be sure to test exception conditions - consumers will need these guarantees.

To run the suite of system tests for the project, ensure you have activated your virtualenv and run:

    $ make test           # run all tests, linter, and coverage tool

# Making and Submitting Changes

Once you become familiar with the API and what it does (see the Reference Documentation section below), then you should be able to easily make changes to this library.  The layout of the packages and the unit tests is designed to be intuitive and follow the API as closely as possible.

This library has a single branch called `master`; to make a change, simply create a feature branch off of `master`.  When finished, submit a pull request against `master`.  Simple!

Depending on the nature of your change, you may want to reference or add to `tests/sample_data/arns.py`, a comprehensive database of known ARN patterns and mappings.

Lastly, before submitting your change, bump the version number found here: `__version__.py` using the [semver](https://semver.org/spec/v2.0.0.html) paradigm.  Most changes will be patch versions.  You should avoid making major version (breaking) changes to the API if at all possible.  You will be unable to release your change if you neglect to change the version; thankfully the problem will be obvious should you forget - github will complain during the build.


# Tips and Tricks

1. The unit tests utilize `pytest.mark` to categorize tests.  You'll note that there are presently two main categories - `unit` and `performance`.  The former denote quick maximum-coverage tests with focused scope.  The latter tests performance characteristics by running the API many thousands of times a second.  New APIs should have both kinds of tests.  In most circumstances, you will not need to write new performance tests.

1. You can run `make clean` at any time from the terminal.  The inverse of `make init`, this command will clean up all temp files and uninstall all dependencies.  Useful if you feel you've made a mess but you don't want to re-clone the whole repository.

1. Running `make lint` will run the linter by itself. Running `make lint-fix` will attempt to autofix many linter errors related to formatting and whitespace.  Other linter errors like unused imports or missing headers will need to be fixed by hand.

1. Running `make help` will list all of the available make commands if you forget.


# API Reference Documentation

The following identifiers are covered in detail in the following main sections.  You'll get information on basic concepts, API usage, and advanced deep-dives.
* [CloudZero Resource Names (CZRN)](docs/czrn.md)
* [CloudZero Relationships (CZREL)](docs/czrel.md)
* [CloudZero Statistic IDs](docs/statistic-id.md)
