Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add command to create new integrations #2037

Merged
merged 10 commits into from
Aug 20, 2018
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions datadog_checks_dev/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ and is available on Linux, macOS, and Windows, and supports Python 2.7/3.5+ and
- [Set](#set)
- [Show](#show)
- [Update](#update)
- [Create](#create)
- [Dep](#dep)
- [Freeze](#freeze)
- [Pin](#pin)
Expand Down Expand Up @@ -217,6 +218,22 @@ Options:
-h, --help Show this message and exit.
```

#### Create

```console
$ ddev create -h
Usage: ddev create [OPTIONS] NAME

Create a new integration.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't actually create a new Integration - it sets up the framework for a new Integration. We should be clear about that here and in the Options: below.


Options:
-t, --type [check] The type of integration to create
-l, --location TEXT Where to create the integration
-q, --quiet Show less output
-n, --dry-run Only show what would be created
-h, --help Show this message and exit.
```

#### Dep

```console
Expand Down
9 changes: 9 additions & 0 deletions datadog_checks_dev/datadog_checks/dev/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# (C) Datadog, Inc. 2018
# All rights reserved
# Licensed under a 3-clause BSD style license (see LICENSE)
import sys

from .tooling.cli import ddev


sys.exit(ddev())
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# Licensed under a 3-clause BSD style license (see LICENSE)
from .clean import clean
from .config import config
from .create import create
from .dep import dep
from .manifest import manifest
from .release import release
Expand All @@ -11,6 +12,7 @@
ALL_COMMANDS = (
clean,
config,
create,
dep,
manifest,
release,
Expand Down
164 changes: 164 additions & 0 deletions datadog_checks_dev/datadog_checks/dev/tooling/commands/create.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
# (C) Datadog, Inc. 2018
# All rights reserved
# Licensed under a 3-clause BSD style license (see LICENSE)
import os
import uuid
from collections import defaultdict
from datetime import datetime

import click

from .utils import CONTEXT_SETTINGS, abort, echo_info, echo_success
from ..constants import get_root
from ..create import create_template_files, get_valid_templates
from ..utils import normalize_package_name
from ...utils import resolve_path

HYPHEN = b'\xe2\x94\x80\xe2\x94\x80'.decode('utf-8')
PIPE = b'\xe2\x94\x82'.decode('utf-8')
PIPE_MIDDLE = b'\xe2\x94\x9c'.decode('utf-8')
PIPE_END = b'\xe2\x94\x94'.decode('utf-8')


def tree():
return defaultdict(tree)


def construct_output_info(path, depth, last, is_dir=False):
if depth == 0:
return u'', path, is_dir
else:
if depth == 1:
return (
u'{}{} '.format(
PIPE_END if last else PIPE_MIDDLE, HYPHEN
),
path,
is_dir
)
else:
return (
u'{} {}{}'.format(
PIPE,
u' ' * 4 * (depth - 2),
u'{}{} '.format(PIPE_END if last or is_dir else PIPE_MIDDLE, HYPHEN)
),
path,
is_dir
)


def path_tree_output(path_tree, depth=0):
# Avoid possible imposed recursion limits by using a generator.
# See https://en.wikipedia.org/wiki/Trampoline_(computing)
dirs = []
files = []

for path in path_tree:
if len(path_tree[path]) > 0:
dirs.append(path)
else:
files.append(path)

dirs.sort()
length = len(dirs)

for i, path in enumerate(dirs, 1):
yield construct_output_info(path, depth, last=i == length and not files, is_dir=True)

for info in path_tree_output(path_tree[path], depth + 1):
yield info

files.sort()
length = len(files)

for i, path in enumerate(files, 1):
yield construct_output_info(path, depth, last=i == length)


def display_path_tree(path_tree):
for indent, path, is_dir in path_tree_output(path_tree):
if indent:
echo_info(indent, nl=False)

if is_dir:
echo_success(path)
else:
echo_info(path)


@click.command(context_settings=CONTEXT_SETTINGS, short_help='Create a new integration')
@click.argument('name')
@click.option(
'--type', '-t', 'integration_type',
type=click.Choice(get_valid_templates()),
default='check',
help='The type of integration to create'
)
@click.option('--location', '-l', help='Where to create the integration')
@click.option('--quiet', '-q', is_flag=True, help='Show less output')
@click.option('--dry-run', '-n', is_flag=True, help='Only show what would be created')
@click.pass_context
def create(ctx, name, integration_type, location, quiet, dry_run):
"""Create a new integration."""
integration_name = normalize_package_name(name)
Copy link
Contributor

@zippolyte zippolyte Aug 17, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should also keep a reference to name as is, and use it as the display name in README and so on, instead of check_name_cap

root = resolve_path(location) if location else get_root()
path_sep = os.path.sep

integration_dir = os.path.join(root, integration_name)
if os.path.exists(integration_dir):
abort('Path `{}` already exists!'.format(integration_dir))

repo_choice = ctx.obj['repo_choice']
if repo_choice == 'core':
author = 'Datadog'
email = 'help@datadoghq.com'
email_packages = 'packages@datadoghq.com'
support_type = 'core'
tox_base_dep = '../datadog_checks_base[deps]'
else:
author = 'U.N. Owen'
email = email_packages = 'friend@datadog.community'
support_type = 'contrib'
tox_base_dep = 'datadog-checks-base[deps]'

config = {
'author': author,
'check_class': '{}Check'.format(''.join(part.capitalize() for part in integration_name.split('_'))),
'check_name': integration_name,
'check_name_cap': integration_name.capitalize(),
'email': email,
'email_packages': email_packages,
'guid': uuid.uuid4(),
'repo_choice': repo_choice,
'support_type': support_type,
'tox_base_dep': tox_base_dep,
'year': str(datetime.now().year),
}

files = create_template_files(integration_type, root, config, read=not dry_run)
file_paths = [file.file_path.replace('{}{}'.format(root, path_sep), '', 1) for file in files]

path_tree = tree()
for file_path in file_paths:
branch = path_tree

for part in file_path.split(path_sep):
branch = branch[part]

if dry_run:
if quiet:
echo_info('Will create `{}`'.format(integration_dir))
else:
echo_info('Will create in `{}`:'.format(root))
display_path_tree(path_tree)
return

for file in files:
file.write()

if quiet:
echo_info('Created `{}`'.format(integration_dir))
else:
echo_info('Created in `{}`:'.format(root))
display_path_tree(path_tree)
77 changes: 77 additions & 0 deletions datadog_checks_dev/datadog_checks/dev/tooling/create.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# (C) Datadog, Inc. 2018
# All rights reserved
# Licensed under a 3-clause BSD style license (see LICENSE)
import os

from ..utils import (
create_file,
dir_exists,
ensure_parent_dir_exists,
path_join,
read_file,
read_file_binary,
write_file,
write_file_binary
)

TEMPLATES_DIR = path_join(os.path.dirname(os.path.abspath(__file__)), 'templates')
BINARY_EXTENSIONS = ('.png', )


def get_valid_templates():
return sorted(os.listdir(TEMPLATES_DIR))


def create_template_files(template_name, new_root, config, read=False):
files = []

template_root = path_join(TEMPLATES_DIR, template_name)
if not dir_exists(template_root):
return files

for root, _, template_files in os.walk(template_root):
for template_file in template_files:
template_path = path_join(root, template_file)

file_path = template_path.replace(template_root, '')
file_path = '{}{}'.format(new_root, file_path.format(**config))

files.append(
File(
file_path,
template_path,
config,
read=read
)
)

return files


class File(object):
def __init__(self, file_path, template_path, config, read=False):
self.file_path = file_path
self.template_path = template_path
self.config = config
self.binary = template_path.endswith(BINARY_EXTENSIONS)
self._read = read_file_binary if self.binary else read_file
self._write = write_file_binary if self.binary else write_file
self.contents = None

if read:
self.read()

def read(self):
contents = self._read(self.template_path)

if self.binary:
self.contents = contents
else:
self.contents = contents.format(**self.config)

def write(self):
if self.contents is None:
create_file(self.file_path)
else:
ensure_parent_dir_exists(self.file_path)
self._write(self.file_path, self.contents)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# CHANGELOG - {check_name_cap}

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
graft datadog_checks
graft tests

include MANIFEST.in
include README.md
include requirements.in
include requirements.txt
include requirements-dev.txt
include manifest.json

global-exclude *.py[cod] __pycache__
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Agent Check: {check_name_cap}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a line break between these headers.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

## Overview

This check monitors [{check_name_cap}][1].

## Setup

### Installation

The {check_name_cap} check is included in the [Datadog Agent][2] package, so you don't
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is true of core Integrations, but not of extras; suggest hedging our default language here.

need to install anything else on your server.

### Configuration

1. Edit the `{check_name}.d/conf.yaml` file, in the `conf.d/` folder at the root of your
Agent's configuration directory to start collecting your {check_name} performance data.
See the [sample {check_name}.d/conf.yaml][3] for all available configuration options.

2. [Restart the Agent][4]

### Validation

[Run the Agent's `status` subcommand][5] and look for `{check_name}` under the Checks section.

## Data Collected
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line break please :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

### Metrics

The {check_name_cap} check does not include any metrics at this time.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need to say "at this time" here. The documentation represents now, not a hypothetical future.

The {check_name_cap} check does not include any metrics.

Imho, we could even shorten this to:

{check_name_cap} does not include any metrics.


### Service Checks

The {check_name_cap} check does not include any service checks at this time.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above.


### Events

The {check_name_cap} check does not include any events at this time.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above.


## Troubleshooting

Need help? Contact [Datadog Support][6].

[1]: link to integration's site
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a templatable item as well? If that's problematic, we should surface this more obviously, like** LINK_TO_INTEGERATION_SITE ** or something 😄

[2]: https://app.datadoghq.com/account/settings#agent
[3]: https://github.com/DataDog/integrations-core/blob/master/{check_name}/datadog_checks/{check_name}/data/conf.yaml.example
[4]: https://docs.datadoghq.com/agent/faq/agent-commands/#start-stop-restart-the-agent
[5]: https://docs.datadoghq.com/agent/faq/agent-commands/#agent-status-and-information
[6]: https://docs.datadoghq.com/help/
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# (C) Datadog, Inc. {year}
# All rights reserved
# Licensed under a 3-clause BSD style license (see LICENSE)
__path__ = __import__('pkgutil').extend_path(__path__, __name__)
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# (C) Datadog, Inc. {year}
# All rights reserved
# Licensed under a 3-clause BSD style license (see LICENSE)
__version__ = '0.0.1'
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# (C) Datadog, Inc. {year}
# All rights reserved
# Licensed under a 3-clause BSD style license (see LICENSE)
from .__about__ import __version__
from .{check_name} import {check_class}

__all__ = [
'__version__',
'{check_class}'
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
init_config:

instances:
- {{}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# (C) Datadog, Inc. {year}
# All rights reserved
# Licensed under a 3-clause BSD style license (see LICENSE)
from datadog_checks.checks import AgentCheck


class {check_class}(AgentCheck):
def check(self, instance):
pass
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading