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 smtp relation interface #126

Merged
merged 34 commits into from
Dec 15, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
035e8a1
Add smtp interface
arturo-seijas Dec 4, 2023
c92a565
Add smtp interface
arturo-seijas Dec 4, 2023
01cb82a
Add smtp interface
arturo-seijas Dec 4, 2023
d4487e3
Add smtp interface
arturo-seijas Dec 4, 2023
bde7546
Add smtp interface
arturo-seijas Dec 4, 2023
6495359
Fix linting
arturo-seijas Dec 4, 2023
3ef1c7b
Fix json schemas
arturo-seijas Dec 5, 2023
afdefe8
Add password field
arturo-seijas Dec 5, 2023
7af8105
Fix password field, docs and linting
arturo-seijas Dec 5, 2023
6ae6c16
Fix json schemas
arturo-seijas Dec 5, 2023
d8669e6
Update interfaces/smtp/v0/README.md
arturo-seijas Dec 5, 2023
bb39ee5
Improve descriptions
arturo-seijas Dec 5, 2023
79dc835
Fix json schemas
arturo-seijas Dec 5, 2023
1805a2c
Fix password_id in schema example
arturo-seijas Dec 11, 2023
ce41444
Fix schema docstrings
arturo-seijas Dec 11, 2023
2d9c04b
Fix readme
arturo-seijas Dec 11, 2023
8aca137
Add more port examples to the schema
arturo-seijas Dec 11, 2023
59b8ed1
Change domain example in schema
arturo-seijas Dec 11, 2023
c0e5bbf
Change domain examples
arturo-seijas Dec 11, 2023
2ce21b4
Change domain examples
arturo-seijas Dec 11, 2023
e00912c
Fix expectations in readme
arturo-seijas Dec 11, 2023
a2d7443
Build schema
arturo-seijas Dec 11, 2023
6b8e1e8
Expend schema descriptions
arturo-seijas Dec 11, 2023
6938456
Expend schema descriptions
arturo-seijas Dec 11, 2023
2393fc8
Expend schema descriptions
arturo-seijas Dec 11, 2023
50f312e
Fix schema definition
arturo-seijas Dec 11, 2023
9483efa
Fix schema definition
arturo-seijas Dec 11, 2023
64b793b
Fix domain description
arturo-seijas Dec 12, 2023
65fb500
Fix domain description
arturo-seijas Dec 12, 2023
697f503
Update interfaces/smtp/v0/schema.py
arturo-seijas Dec 14, 2023
db05bff
Merge branch 'main' into main
arturo-seijas Dec 14, 2023
30c2579
Remove accidentally commited file
arturo-seijas Dec 14, 2023
a3ebb51
Fix schemas
arturo-seijas Dec 14, 2023
67b9c62
Merge branch 'main' into main
arturo-seijas Dec 15, 2023
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ To quickly get started, see the [template interface](https://github.com/canonica
| | [`auth_proxy`](interfaces/auth_proxy/v0/README.md) | ![Status: Draft](https://img.shields.io/badge/Status-Draft-orange) |
| | [`openfga`](interfaces/openfga/v0/README.md) | ![Status: Live](https://img.shields.io/badge/Status-Live-darkgreen) |
| | [`saml`](interfaces/saml/v0/README.md) | ![Status: Live](https://img.shields.io/badge/Status-Live-darkgreen) |
| | [`smtp`](interfaces/smtp/v0/README.md) | ![Status: Live](https://img.shields.io/badge/Status-Live-darkgreen) |
arturo-seijas marked this conversation as resolved.
Show resolved Hide resolved
| Observability | [`grafana_auth`](interfaces/grafana_auth/v0/README.md) | ![Status: Draft](https://img.shields.io/badge/Status-Draft-orange) |
| | [`prometheus_remote_write`](interfaces/prometheus_remote_write/v0/README.md) | ![Status: Live](https://img.shields.io/badge/Status-Live-darkgreen) |
| | [`prometheus_scrape`](interfaces/prometheus_scrape/v0/README.md) | ![Status: Live](https://img.shields.io/badge/Status-Live-darkgreen) |
Expand Down
129 changes: 129 additions & 0 deletions docs/json_schemas/smtp/v0/provider.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
{
simskij marked this conversation as resolved.
Show resolved Hide resolved
"title": "ProviderSchema",
"description": "Provider schema for SMTP.",
"type": "object",
"properties": {
"unit": {
"$ref": "#/definitions/BaseModel"
},
"app": {
"$ref": "#/definitions/SmtpProviderData"
}
},
"required": [
"app"
],
"definitions": {
"BaseModel": {
"title": "BaseModel",
"type": "object",
"properties": {}
},
"AuthType": {
"title": "AuthType",
"description": "Represent the auth type values.\n\nAttributes:\n NONE: none\n NOT_PROVIDED: not_provided\n PLAIN: plain",
"enum": [
"none",
"not_provided",
"plain"
],
"type": "string"
},
"TransportSecurity": {
"title": "TransportSecurity",
"description": "Represent the transport security values.\n\nAttributes:\n NONE: none\n STARTTLS: starttls\n TLS: tls",
"enum": [
"none",
"starttls",
"tls"
],
"type": "string"
},
"SmtpProviderData": {
"title": "SmtpProviderData",
"type": "object",
"properties": {
"host": {
"title": "Host",
"description": "SMTP host.",
"minLength": 1,
"examples": [
"example.smtp"
],
"type": "string"
},
"port": {
"title": "Port",
"description": "SMTP port.",
"minimum": 1,
"maximum": 65536,
"examples": [
25
],
"type": "integer"
},
"user": {
"title": "User",
"description": "SMTP user.",
"examples": [
"some_user"
],
"type": "string"
},
"password": {
"title": "Password",
"description": "SMTP password.",
"examples": [
"somepasswd"
],
"type": "string"
},
"password_id": {
"title": "Password ID",
"description": "Secret ID for the SMTP password.",
"examples": [
"secret:123213123123123123123"
],
"type": "string"
},
"auth_type": {
"title": "Auth type",
"description": "The type used to authenticate with the SMTP relay.",
"examples": [
"none"
],
"allOf": [
{
"$ref": "#/definitions/AuthType"
}
]
},
"transport_security": {
"title": "Transport security",
"description": "The security protocol to use for the outgoing SMTP relay.",
"examples": [
"none"
],
"allOf": [
{
"$ref": "#/definitions/TransportSecurity"
}
]
},
"domain": {
"title": "Domain",
"description": "The domain used by the sent emails from SMTP relay.",
"examples": [
"domain"
],
"type": "string"
}
},
"required": [
"host",
"auth_type",
"transport_security"
]
}
}
}
20 changes: 20 additions & 0 deletions docs/json_schemas/smtp/v0/requirer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"title": "RequirerSchema",
"description": "Requirer schema for SMTP.",
"type": "object",
"properties": {
"unit": {
"$ref": "#/definitions/BaseModel"
},
"app": {
"$ref": "#/definitions/BaseModel"
}
},
"definitions": {
"BaseModel": {
"title": "BaseModel",
"type": "object",
"properties": {}
}
}
}
64 changes: 64 additions & 0 deletions interfaces/smtp/v0/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# `smtp`

## Overview

This relation interface specification describes the expected behavior of charms integrating over the `smtp` Juju interface to provide or consume SMTP data.

## Usage

In most cases, this will be accomplished using the [smtp library](https://github.com/canonical/smtp-integrator-operator/blob/main/lib/charms/smtp_integrator/v0/smtp.py), although charm developers are free to provide alternative libraries as long as they fulfil the behavioural and schematic requirements described in this document.

## Direction

The `smtp` interface implements a provider/requirer pattern.
The requirer is a charm that requires SMTP details to connect to an SMTP server, and the provider is a charm holding those details.

```mermaid
flowchart TD
Provider -- host, port, user, password_id, auth_type, transport_security, domain --> Requirer
arturo-seijas marked this conversation as resolved.
Show resolved Hide resolved
arturo-seijas marked this conversation as resolved.
Show resolved Hide resolved
```

or alternatively, if secrets are not supported by both sides
arturo-seijas marked this conversation as resolved.
Show resolved Hide resolved

```mermaid
flowchart TD
Provider -- host, port, user, password, auth_type, transport_security, domain --> Requirer
```

## Behavior

The requirer and the provider must adhere to a certain set of criteria to be considered compatible with the interface.

### Provider

- Is expected to publish the `host` in the relation databag.
- Is expected to publish the `port` in the relation databag.
- Is expected to publish the `auth_type` in the relation databag.
- Is expected to publish the `transport_security` in the relation databag.
arturo-seijas marked this conversation as resolved.
Show resolved Hide resolved

### Requirer

- Is not expected to publish anything

## Relation Data

### Provider

[\[Pydantic Schema\]](./schema.py)

Provider publishes the SMTP configuration. It should be placed in the **application** databag.

#### Example

```yaml
related-units: {}
application_data: {
"host": "example.smtp",
"port": "25",
"user": "example_user",
"password_id": "01548499c9233d4612352c989162d940f6a9e6f6d5cc058dfcf66f51575e09c2",
"auth_type": "plain",
"transport_security": "tls",
"domain": "domain",
arturo-seijas marked this conversation as resolved.
Show resolved Hide resolved
}
```
6 changes: 6 additions & 0 deletions interfaces/smtp/v0/charms.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
providers:
- name: smtp-integrator
url: https://github.com/canonical/smtp-integrator-operator

requirers:
[]
19 changes: 19 additions & 0 deletions interfaces/smtp/v0/interface_tests/test_provider.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright 2023 Canonical
# See LICENSE file for licensing details.
from interface_tester.interface_test import Tester
from scenario import Relation, State


def test_data_published_on_created():
t = Tester(
State(
relations=[
Relation(
endpoint="smtp",
interface="smtp",
)
],
)
)
t.run("smtp-relation-created")
t.assert_schema_valid()
112 changes: 112 additions & 0 deletions interfaces/smtp/v0/schema.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# Copyright 2023 Canonical
# See LICENSE file for licensing details.
"""This file defines the schema for the provider side of the smtp interface.

It exposes one interfaces.schema_base.DataBagSchema subclass called:
- ProviderSchema

Examples:
ProviderSchema:
unit: <empty>
app: {"smtp":
{
"host": "example.smtp",
"port": "25",
"user": "example_user",
"password_id": "01548499c9233d4612352c989162d940f6a9e6f6d5cc058dfcf66f51575e09c2",
arturo-seijas marked this conversation as resolved.
Show resolved Hide resolved
"auth_type": "plain",
"transport_security": "tls",
"domain": "domain",
}
}
"""
from enum import Enum
from interface_tester.schema_base import DataBagSchema
from pydantic import BaseModel, Field
from typing import Optional


class TransportSecurity(str, Enum):
"""Represent the transport security values.

Attributes:
NONE: none
STARTTLS: starttls
TLS: tls
arturo-seijas marked this conversation as resolved.
Show resolved Hide resolved
"""

NONE = "none"
STARTTLS = "starttls"
TLS = "tls"


class AuthType(str, Enum):
"""Represent the auth type values.

Attributes:
NONE: none
NOT_PROVIDED: not_provided
PLAIN: plain
arturo-seijas marked this conversation as resolved.
Show resolved Hide resolved
"""

NONE = "none"
NOT_PROVIDED = "not_provided"
PLAIN = "plain"

arturo-seijas marked this conversation as resolved.
Show resolved Hide resolved

class SmtpProviderData(BaseModel):
host: str = Field(
...,
arturo-seijas marked this conversation as resolved.
Show resolved Hide resolved
min_length=1,
description="SMTP host.",
title="Host",
examples=["example.smtp"],
arturo-seijas marked this conversation as resolved.
Show resolved Hide resolved
)
port: int = Field(
None,
arturo-seijas marked this conversation as resolved.
Show resolved Hide resolved
ge=1,
le=65536,
description="SMTP port.",
title="Port",
examples=[25],
arturo-seijas marked this conversation as resolved.
Show resolved Hide resolved
)
user: Optional[str] = Field(
arturo-seijas marked this conversation as resolved.
Show resolved Hide resolved
description="SMTP user.",
title="User",
examples=["some_user"],
)
password: Optional[str] = Field(
description="SMTP password.",
arturo-seijas marked this conversation as resolved.
Show resolved Hide resolved
title="Password",
examples=["somepasswd"],
)
password_id: Optional[str] = Field(
arturo-seijas marked this conversation as resolved.
Show resolved Hide resolved
arturo-seijas marked this conversation as resolved.
Show resolved Hide resolved
description="Secret ID for the SMTP password.",
title="Password ID",
examples=["secret:123213123123123123123"],
)
auth_type: AuthType = Field(
description="The type used to authenticate with the SMTP relay.",
title="Auth type",
examples=[AuthType.NONE],
)
transport_security: TransportSecurity = Field(
description="The security protocol to use for the outgoing SMTP relay.",
arturo-seijas marked this conversation as resolved.
Show resolved Hide resolved
title="Transport security",
examples=[TransportSecurity.NONE],
)
domain: Optional[str] = Field(
description="The domain used by the sent emails from SMTP relay.",
arturo-seijas marked this conversation as resolved.
Show resolved Hide resolved
title="Domain",
examples=["domain"],
arturo-seijas marked this conversation as resolved.
Show resolved Hide resolved
)


class ProviderSchema(DataBagSchema):
"""Provider schema for SMTP."""

app: SmtpProviderData


class RequirerSchema(DataBagSchema):
arturo-seijas marked this conversation as resolved.
Show resolved Hide resolved
"""Requirer schema for SMTP."""