tinybird 0.0.1.dev99__tar.gz → 0.0.1.dev100__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of tinybird might be problematic. Click here for more details.
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/PKG-INFO +1 -1
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/__cli__.py +2 -2
- tinybird-0.0.1.dev100/tinybird/tb/modules/infra.py +564 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird.egg-info/PKG-INFO +1 -1
- tinybird-0.0.1.dev99/tinybird/tb/modules/infra.py +0 -693
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/setup.cfg +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/__cli__.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/ch_utils/constants.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/ch_utils/engine.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/check_pypi.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/client.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/config.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/connectors.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/context.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/datafile.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/datatypes.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/feedback_manager.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/git_settings.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/prompts.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/sql.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/sql_template.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/sql_template_fmt.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/sql_toolset.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/syncasync.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/cli.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/auth.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/build.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/cicd.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/cli.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/common.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/config.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/connection.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/copy.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/create.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/datafile/build.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/datafile/build_common.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/datafile/build_datasource.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/datafile/build_pipe.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/datafile/common.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/datafile/diff.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/datafile/exceptions.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/datafile/fixture.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/datafile/format_common.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/datafile/format_datasource.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/datafile/format_pipe.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/datafile/parse_datasource.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/datafile/parse_pipe.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/datafile/pipe_checker.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/datafile/playground.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/datafile/pull.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/datasource.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/deployment.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/endpoint.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/exceptions.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/feedback_manager.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/fmt.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/job.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/llm.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/llm_utils.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/local.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/local_common.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/login.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/logout.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/materialization.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/mock.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/open.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/pipe.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/playground.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/project.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/regions.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/secret.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/shell.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/table.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/tag.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/telemetry.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/test.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/tinyunit/tinyunit.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/tinyunit/tinyunit_lib.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/token.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/watch.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/workspace.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb/modules/workspace_members.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb_cli.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb_cli_modules/auth.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb_cli_modules/branch.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb_cli_modules/cicd.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb_cli_modules/cli.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb_cli_modules/common.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb_cli_modules/config.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb_cli_modules/connection.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb_cli_modules/datasource.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb_cli_modules/exceptions.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb_cli_modules/fmt.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb_cli_modules/job.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb_cli_modules/pipe.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb_cli_modules/regions.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb_cli_modules/tag.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb_cli_modules/telemetry.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb_cli_modules/test.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb_cli_modules/workspace.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tb_cli_modules/workspace_members.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird/tornado_template.py +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird.egg-info/SOURCES.txt +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird.egg-info/dependency_links.txt +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird.egg-info/entry_points.txt +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird.egg-info/requires.txt +0 -0
- {tinybird-0.0.1.dev99 → tinybird-0.0.1.dev100}/tinybird.egg-info/top_level.txt +0 -0
|
@@ -4,5 +4,5 @@ __description__ = 'Tinybird Command Line Tool'
|
|
|
4
4
|
__url__ = 'https://www.tinybird.co/docs/cli/introduction.html'
|
|
5
5
|
__author__ = 'Tinybird'
|
|
6
6
|
__author_email__ = 'support@tinybird.co'
|
|
7
|
-
__version__ = '0.0.1.
|
|
8
|
-
__revision__ = '
|
|
7
|
+
__version__ = '0.0.1.dev100'
|
|
8
|
+
__revision__ = 'ccb1518'
|
|
@@ -0,0 +1,564 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import subprocess
|
|
3
|
+
import time
|
|
4
|
+
import uuid
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
import requests
|
|
10
|
+
from click import Context
|
|
11
|
+
|
|
12
|
+
from tinybird.client import TinyB
|
|
13
|
+
from tinybird.syncasync import async_to_sync
|
|
14
|
+
from tinybird.tb.modules.cli import CLIException, cli
|
|
15
|
+
from tinybird.tb.modules.common import coro, echo_safe_humanfriendly_tables_format_smart_table
|
|
16
|
+
from tinybird.tb.modules.config import CLIConfig
|
|
17
|
+
from tinybird.tb.modules.feedback_manager import FeedbackManager
|
|
18
|
+
|
|
19
|
+
from .common import CONTEXT_SETTINGS
|
|
20
|
+
|
|
21
|
+
K8S_YML = """
|
|
22
|
+
---
|
|
23
|
+
apiVersion: v1
|
|
24
|
+
kind: Namespace
|
|
25
|
+
metadata:
|
|
26
|
+
name: %(namespace)s
|
|
27
|
+
labels:
|
|
28
|
+
name: tinybird
|
|
29
|
+
---
|
|
30
|
+
apiVersion: v1
|
|
31
|
+
kind: ServiceAccount
|
|
32
|
+
metadata:
|
|
33
|
+
name: tinybird
|
|
34
|
+
namespace: %(namespace)s
|
|
35
|
+
labels:
|
|
36
|
+
name: tinybird
|
|
37
|
+
automountServiceAccountToken: true
|
|
38
|
+
---
|
|
39
|
+
apiVersion: v1
|
|
40
|
+
kind: Service
|
|
41
|
+
metadata:
|
|
42
|
+
name: tinybird
|
|
43
|
+
namespace: %(namespace)s
|
|
44
|
+
labels:
|
|
45
|
+
name: tinybird
|
|
46
|
+
spec:
|
|
47
|
+
type: ClusterIP
|
|
48
|
+
ports:
|
|
49
|
+
- port: 80
|
|
50
|
+
targetPort: http
|
|
51
|
+
protocol: TCP
|
|
52
|
+
name: http
|
|
53
|
+
selector:
|
|
54
|
+
name: tinybird
|
|
55
|
+
---
|
|
56
|
+
apiVersion: networking.k8s.io/v1
|
|
57
|
+
kind: Ingress
|
|
58
|
+
metadata:
|
|
59
|
+
namespace: %(namespace)s
|
|
60
|
+
name: tinybird
|
|
61
|
+
annotations:
|
|
62
|
+
alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}]'
|
|
63
|
+
alb.ingress.kubernetes.io/scheme: internet-facing
|
|
64
|
+
alb.ingress.kubernetes.io/ssl-policy: ELBSecurityPolicy-TLS13-1-2-2021-06
|
|
65
|
+
alb.ingress.kubernetes.io/ssl-redirect: '443'
|
|
66
|
+
alb.ingress.kubernetes.io/target-type: 'ip'
|
|
67
|
+
alb.ingress.kubernetes.io/load-balancer-name: %(namespace)s
|
|
68
|
+
alb.ingress.kubernetes.io/success-codes: '200,301,302'
|
|
69
|
+
spec:
|
|
70
|
+
ingressClassName: alb
|
|
71
|
+
tls:
|
|
72
|
+
- hosts:
|
|
73
|
+
- %(full_dns_name)s
|
|
74
|
+
rules:
|
|
75
|
+
- http:
|
|
76
|
+
paths:
|
|
77
|
+
- path: /
|
|
78
|
+
pathType: Prefix
|
|
79
|
+
backend:
|
|
80
|
+
service:
|
|
81
|
+
name: tinybird
|
|
82
|
+
port:
|
|
83
|
+
name: http
|
|
84
|
+
---
|
|
85
|
+
apiVersion: apps/v1
|
|
86
|
+
kind: StatefulSet
|
|
87
|
+
metadata:
|
|
88
|
+
name: tinybird
|
|
89
|
+
namespace: %(namespace)s
|
|
90
|
+
spec:
|
|
91
|
+
serviceName: "tinybird"
|
|
92
|
+
replicas: 1
|
|
93
|
+
selector:
|
|
94
|
+
matchLabels:
|
|
95
|
+
name: tinybird
|
|
96
|
+
template:
|
|
97
|
+
metadata:
|
|
98
|
+
labels:
|
|
99
|
+
name: tinybird
|
|
100
|
+
spec:
|
|
101
|
+
serviceAccountName: tinybird
|
|
102
|
+
containers:
|
|
103
|
+
- name: tinybird
|
|
104
|
+
image: "tinybirdco/tinybird-local:beta"
|
|
105
|
+
imagePullPolicy: Always
|
|
106
|
+
ports:
|
|
107
|
+
- name: http
|
|
108
|
+
containerPort: 7181
|
|
109
|
+
protocol: TCP
|
|
110
|
+
env:
|
|
111
|
+
- name: TB_INFRA_TOKEN
|
|
112
|
+
value: "%(infra_token)s"
|
|
113
|
+
- name: TB_INFRA_WORKSPACE
|
|
114
|
+
value: "%(infra_workspace)s"
|
|
115
|
+
- name: TB_INFRA_ORGANIZATION
|
|
116
|
+
value: "%(infra_organization)s"
|
|
117
|
+
- name: TB_INFRA_USER
|
|
118
|
+
value: "%(infra_user)s"
|
|
119
|
+
volumeMounts:
|
|
120
|
+
- name: clickhouse-data
|
|
121
|
+
mountPath: /var/lib/clickhouse
|
|
122
|
+
volumeClaimTemplates:
|
|
123
|
+
- metadata:
|
|
124
|
+
name: clickhouse-data
|
|
125
|
+
spec:
|
|
126
|
+
accessModes: [ "ReadWriteOnce" ]
|
|
127
|
+
resources:
|
|
128
|
+
requests:
|
|
129
|
+
storage: 100Gi
|
|
130
|
+
storageClassName: %(storage_class)s
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
TERRAFORM_FIRST_TEMPLATE = """
|
|
134
|
+
terraform {
|
|
135
|
+
required_providers {
|
|
136
|
+
aws = {
|
|
137
|
+
source = "hashicorp/aws"
|
|
138
|
+
version = "~> 5.0"
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
provider "aws" {
|
|
144
|
+
region = "%(aws_region)s"
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
# Get the hosted zone data
|
|
148
|
+
data "aws_route53_zone" "selected" {
|
|
149
|
+
name = "%(dns_zone_name)s"
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
# Create ACM certificate
|
|
153
|
+
resource "aws_acm_certificate" "cert" {
|
|
154
|
+
domain_name = "%(dns_record)s.${data.aws_route53_zone.selected.name}"
|
|
155
|
+
validation_method = "DNS"
|
|
156
|
+
subject_alternative_names = [data.aws_route53_zone.selected.name]
|
|
157
|
+
|
|
158
|
+
lifecycle {
|
|
159
|
+
create_before_destroy = true
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
# Create DNS records for certificate validation
|
|
164
|
+
resource "aws_route53_record" "cert_validation" {
|
|
165
|
+
for_each = {
|
|
166
|
+
for dvo in aws_acm_certificate.cert.domain_validation_options : dvo.domain_name => {
|
|
167
|
+
name = dvo.resource_record_name
|
|
168
|
+
record = dvo.resource_record_value
|
|
169
|
+
type = dvo.resource_record_type
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
allow_overwrite = true
|
|
174
|
+
name = each.value.name
|
|
175
|
+
records = [each.value.record]
|
|
176
|
+
ttl = 60
|
|
177
|
+
type = each.value.type
|
|
178
|
+
zone_id = data.aws_route53_zone.selected.zone_id
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
# Certificate validation
|
|
182
|
+
resource "aws_acm_certificate_validation" "cert" {
|
|
183
|
+
certificate_arn = aws_acm_certificate.cert.arn
|
|
184
|
+
validation_record_fqdns = [for record in aws_route53_record.cert_validation : record.fqdn]
|
|
185
|
+
}
|
|
186
|
+
"""
|
|
187
|
+
|
|
188
|
+
TERRAFORM_SECOND_TEMPLATE = """
|
|
189
|
+
# Create Route 53 record for the load balancer
|
|
190
|
+
data "aws_alb" "tinybird" {
|
|
191
|
+
name = "%(namespace)s"
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
resource "aws_route53_record" "tinybird" {
|
|
195
|
+
zone_id = data.aws_route53_zone.selected.zone_id
|
|
196
|
+
name = "%(full_dns_name)s"
|
|
197
|
+
type = "A"
|
|
198
|
+
alias {
|
|
199
|
+
name = data.aws_alb.tinybird.dns_name
|
|
200
|
+
zone_id = data.aws_alb.tinybird.zone_id
|
|
201
|
+
evaluate_target_health = true
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
output "tinybird_dns" {
|
|
206
|
+
description = "The DNS name for Tinybird"
|
|
207
|
+
value = aws_route53_record.tinybird.fqdn
|
|
208
|
+
}
|
|
209
|
+
"""
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
@cli.group(context_settings=CONTEXT_SETTINGS, hidden=True)
|
|
213
|
+
@click.pass_context
|
|
214
|
+
def infra(ctx: Context) -> None:
|
|
215
|
+
"""Infra commands."""
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
@infra.command(name="init")
|
|
219
|
+
@click.option("--name", type=str, help="Name for identifying the self-managed infrastructure in Tinybird")
|
|
220
|
+
@click.option("--provider", type=str, help="Infrastructure provider (aws, gcp, azure)")
|
|
221
|
+
@click.option("--region", type=str, help="AWS region (for AWS provider)")
|
|
222
|
+
@click.option("--dns-zone-name", type=str, help="DNS zone name")
|
|
223
|
+
@click.option("--namespace", type=str, help="Kubernetes namespace for deployment")
|
|
224
|
+
@click.option("--dns-record", type=str, help="DNS record name to create (without domain, e.g. 'tinybird')")
|
|
225
|
+
@click.option("--storage-class", type=str, help="Storage class for the k8s StatefulSet")
|
|
226
|
+
@click.option(
|
|
227
|
+
"--auto-apply", is_flag=True, help="Automatically apply Terraform and kubectl configuration without prompting"
|
|
228
|
+
)
|
|
229
|
+
@click.option("--skip-apply", is_flag=True, help="Skip Terraform and kubectl configuration and application")
|
|
230
|
+
@click.pass_context
|
|
231
|
+
def infra_init(
|
|
232
|
+
ctx: Context,
|
|
233
|
+
name: str,
|
|
234
|
+
provider: str,
|
|
235
|
+
region: Optional[str] = None,
|
|
236
|
+
dns_zone_name: Optional[str] = None,
|
|
237
|
+
namespace: Optional[str] = None,
|
|
238
|
+
dns_record: Optional[str] = None,
|
|
239
|
+
storage_class: Optional[str] = None,
|
|
240
|
+
auto_apply: bool = False,
|
|
241
|
+
skip_apply: bool = False,
|
|
242
|
+
) -> None:
|
|
243
|
+
"""Init infra"""
|
|
244
|
+
# Check if provider is specified
|
|
245
|
+
if not provider:
|
|
246
|
+
click.echo("Error: --provider option is required. Please specify a provider (aws, gcp, azure).")
|
|
247
|
+
return
|
|
248
|
+
|
|
249
|
+
# AWS-specific Terraform template creation
|
|
250
|
+
if provider.lower() != "aws":
|
|
251
|
+
click.echo("Provider not supported yet.")
|
|
252
|
+
return
|
|
253
|
+
|
|
254
|
+
# Create infra directory if it doesn't exist
|
|
255
|
+
infra_dir = Path(f"infra/{provider}")
|
|
256
|
+
infra_dir.mkdir(exist_ok=True)
|
|
257
|
+
yaml_path = infra_dir / "k8s.yaml"
|
|
258
|
+
tf_path = infra_dir / "main.tf"
|
|
259
|
+
config_path = infra_dir / "config.json"
|
|
260
|
+
|
|
261
|
+
# Load existing configuration if available
|
|
262
|
+
config = {}
|
|
263
|
+
if config_path.exists():
|
|
264
|
+
try:
|
|
265
|
+
with open(config_path, "r") as f:
|
|
266
|
+
config = json.load(f)
|
|
267
|
+
click.echo("Loaded existing configuration from config.json")
|
|
268
|
+
except json.JSONDecodeError:
|
|
269
|
+
click.echo("Warning: Could not parse existing config.json, will create a new one")
|
|
270
|
+
|
|
271
|
+
# Generate a random ID for default values
|
|
272
|
+
random_id = str(uuid.uuid4())[:8]
|
|
273
|
+
|
|
274
|
+
# Get or prompt for configuration values
|
|
275
|
+
name = name or click.prompt("Enter name", type=str)
|
|
276
|
+
region = region or config.get("region") or click.prompt("Enter aws region", default="us-east-1", type=str)
|
|
277
|
+
dns_zone_name = dns_zone_name or config.get("dns_zone_name") or click.prompt("Enter DNS zone name", type=str)
|
|
278
|
+
namespace = (
|
|
279
|
+
namespace
|
|
280
|
+
or config.get("namespace")
|
|
281
|
+
or click.prompt("Enter namespace name", default=f"tinybird-{random_id}", type=str)
|
|
282
|
+
)
|
|
283
|
+
dns_record = (
|
|
284
|
+
dns_record
|
|
285
|
+
or config.get("dns_record")
|
|
286
|
+
or click.prompt("Enter DNS record name (without domain)", default=f"tinybird-{random_id}", type=str)
|
|
287
|
+
)
|
|
288
|
+
storage_class = config.get("storage_class") or click.prompt(
|
|
289
|
+
"Enter storage class", default="gp3-encrypted", type=str
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
# Save configuration
|
|
293
|
+
config = {
|
|
294
|
+
"provider": provider,
|
|
295
|
+
"region": region,
|
|
296
|
+
"dns_zone_name": dns_zone_name,
|
|
297
|
+
"namespace": namespace,
|
|
298
|
+
"dns_record": dns_record,
|
|
299
|
+
"storage_class": storage_class,
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
with open(config_path, "w") as f:
|
|
303
|
+
json.dump(config, f, indent=2)
|
|
304
|
+
|
|
305
|
+
click.echo(f"Configuration saved to {config_path}")
|
|
306
|
+
|
|
307
|
+
client: TinyB = ctx.obj["client"]
|
|
308
|
+
cli_config = CLIConfig.get_project_config()
|
|
309
|
+
user_client = cli_config.get_client(token=cli_config.get_user_token() or "")
|
|
310
|
+
user_workspaces = async_to_sync(user_client.user_workspaces_with_organization)()
|
|
311
|
+
admin_org_id = user_workspaces.get("organization_id")
|
|
312
|
+
admin_org_name = user_workspaces.get("organization_name", "")
|
|
313
|
+
infras = async_to_sync(client.infra_list)(organization_id=admin_org_id)
|
|
314
|
+
infra = next((infra for infra in infras if infra["name"] == name), None)
|
|
315
|
+
if not infra:
|
|
316
|
+
click.echo(FeedbackManager.highlight(message=f"\n» Creating infrastructure '{name}' in Tinybird..."))
|
|
317
|
+
host = f"https://{dns_record}.{dns_zone_name}"
|
|
318
|
+
infra = async_to_sync(client.infra_create)(organization_id=admin_org_id, name=name, host=host)
|
|
319
|
+
|
|
320
|
+
infra_token = infra["token"]
|
|
321
|
+
|
|
322
|
+
# Write the Terraform template
|
|
323
|
+
terraform_content = TERRAFORM_FIRST_TEMPLATE % {
|
|
324
|
+
"aws_region": region,
|
|
325
|
+
"dns_zone_name": dns_zone_name,
|
|
326
|
+
"dns_record": dns_record,
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
with open(tf_path, "w") as f:
|
|
330
|
+
f.write(terraform_content.lstrip())
|
|
331
|
+
|
|
332
|
+
click.echo(f"Created Terraform configuration in {tf_path}")
|
|
333
|
+
|
|
334
|
+
new_content = K8S_YML % {
|
|
335
|
+
"namespace": namespace,
|
|
336
|
+
"storage_class": storage_class,
|
|
337
|
+
"full_dns_name": f"{dns_record}.{dns_zone_name}",
|
|
338
|
+
"infra_token": infra_token,
|
|
339
|
+
"infra_workspace": cli_config.get("name", ""),
|
|
340
|
+
"infra_organization": admin_org_name,
|
|
341
|
+
"infra_user": cli_config.get_user_email() or "",
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
with open(yaml_path, "w") as f:
|
|
345
|
+
f.write(new_content.lstrip())
|
|
346
|
+
|
|
347
|
+
click.echo(f"Created Kubernetes configuration in {yaml_path}")
|
|
348
|
+
|
|
349
|
+
# Apply Terraform configuration if user confirms
|
|
350
|
+
if not skip_apply:
|
|
351
|
+
# Initialize Terraform
|
|
352
|
+
click.echo("Initializing Terraform...")
|
|
353
|
+
init_result = subprocess.run(["terraform", f"-chdir={infra_dir}", "init"], capture_output=True, text=True)
|
|
354
|
+
|
|
355
|
+
if init_result.returncode != 0:
|
|
356
|
+
click.echo("Terraform initialization failed:")
|
|
357
|
+
click.echo(init_result.stderr)
|
|
358
|
+
return
|
|
359
|
+
|
|
360
|
+
# Run terraform plan first
|
|
361
|
+
click.echo("\nRunning Terraform plan...\n")
|
|
362
|
+
plan_result = subprocess.run(["terraform", f"-chdir={infra_dir}", "plan"], capture_output=True, text=True)
|
|
363
|
+
|
|
364
|
+
if plan_result.returncode != 0:
|
|
365
|
+
click.echo("Terraform plan failed:")
|
|
366
|
+
click.echo(plan_result.stderr)
|
|
367
|
+
return
|
|
368
|
+
|
|
369
|
+
click.echo(plan_result.stdout)
|
|
370
|
+
|
|
371
|
+
# Apply Terraform configuration if user confirms
|
|
372
|
+
if auto_apply or click.confirm("Would you like to apply the Terraform configuration now?"):
|
|
373
|
+
click.echo("\nApplying Terraform configuration...\n")
|
|
374
|
+
apply_result = subprocess.run(
|
|
375
|
+
["terraform", f"-chdir={infra_dir}", "apply", "-auto-approve"], capture_output=True, text=True
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
if apply_result.returncode != 0:
|
|
379
|
+
click.echo("Terraform apply failed:")
|
|
380
|
+
click.echo(apply_result.stderr)
|
|
381
|
+
return
|
|
382
|
+
|
|
383
|
+
click.echo(apply_result.stdout)
|
|
384
|
+
|
|
385
|
+
# Prompt to apply the k8s configuration
|
|
386
|
+
if not skip_apply and (
|
|
387
|
+
auto_apply or click.confirm("Would you like to apply the Kubernetes configuration now?")
|
|
388
|
+
):
|
|
389
|
+
# Get current kubectl context
|
|
390
|
+
current_context_result = subprocess.run(
|
|
391
|
+
["kubectl", "config", "current-context"], capture_output=True, text=True
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
current_context = (
|
|
395
|
+
current_context_result.stdout.strip() if current_context_result.returncode == 0 else "unknown"
|
|
396
|
+
)
|
|
397
|
+
# Get available contexts
|
|
398
|
+
contexts_result = subprocess.run(
|
|
399
|
+
["kubectl", "config", "get-contexts", "-o", "name"], capture_output=True, text=True
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
if contexts_result.returncode != 0:
|
|
403
|
+
click.echo("Failed to get kubectl contexts:")
|
|
404
|
+
click.echo(contexts_result.stderr)
|
|
405
|
+
return
|
|
406
|
+
|
|
407
|
+
available_contexts = [context.strip() for context in contexts_result.stdout.splitlines() if context.strip()]
|
|
408
|
+
|
|
409
|
+
if not available_contexts:
|
|
410
|
+
click.echo("No kubectl contexts found. Please configure kubectl first.")
|
|
411
|
+
return
|
|
412
|
+
|
|
413
|
+
# Prompt user to select a context
|
|
414
|
+
if len(available_contexts) == 1:
|
|
415
|
+
selected_context = available_contexts[0]
|
|
416
|
+
click.echo(f"Using the only available kubectl context: {selected_context}")
|
|
417
|
+
else:
|
|
418
|
+
click.echo("\nAvailable kubectl contexts:")
|
|
419
|
+
for i, context in enumerate(available_contexts):
|
|
420
|
+
marker = " (current)" if context == current_context else ""
|
|
421
|
+
click.echo(f" {i + 1}. {context}{marker}")
|
|
422
|
+
|
|
423
|
+
click.echo("")
|
|
424
|
+
default_index = (
|
|
425
|
+
available_contexts.index(current_context) + 1 if current_context in available_contexts else 1
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
selected_index = click.prompt(
|
|
429
|
+
"Select kubectl context number to apply configuration",
|
|
430
|
+
type=click.IntRange(1, len(available_contexts)),
|
|
431
|
+
default=default_index,
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
selected_context = available_contexts[selected_index - 1]
|
|
435
|
+
click.echo(f"Selected context: {selected_context}")
|
|
436
|
+
|
|
437
|
+
# Apply the configuration to the selected context
|
|
438
|
+
click.echo(f"Applying Kubernetes configuration to context '{selected_context}'...")
|
|
439
|
+
apply_result = subprocess.run(
|
|
440
|
+
["kubectl", "--context", selected_context, "apply", "-f", str(yaml_path)],
|
|
441
|
+
capture_output=True,
|
|
442
|
+
text=True,
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
if apply_result.returncode != 0:
|
|
446
|
+
click.echo("Failed to apply Kubernetes configuration:")
|
|
447
|
+
click.echo(apply_result.stderr)
|
|
448
|
+
else:
|
|
449
|
+
click.echo("Kubernetes configuration applied successfully:")
|
|
450
|
+
click.echo(apply_result.stdout)
|
|
451
|
+
|
|
452
|
+
# Get the namespace from the applied configuration
|
|
453
|
+
namespace = None
|
|
454
|
+
with open(yaml_path, "r") as f:
|
|
455
|
+
for line in f:
|
|
456
|
+
if "namespace:" in line and not namespace:
|
|
457
|
+
namespace = line.split("namespace:")[1].strip()
|
|
458
|
+
break
|
|
459
|
+
|
|
460
|
+
if not namespace:
|
|
461
|
+
namespace = "tinybird" # Default namespace
|
|
462
|
+
|
|
463
|
+
click.echo("\nWaiting for load balancer and DNS to be provisioned...")
|
|
464
|
+
|
|
465
|
+
max_attempts = 30 # 30 attempts * 10 seconds = 5 minutes
|
|
466
|
+
endpoint_url = f"https://{dns_record}.{dns_zone_name}"
|
|
467
|
+
|
|
468
|
+
with click.progressbar(
|
|
469
|
+
range(max_attempts),
|
|
470
|
+
label=f"Checking endpoint availability: {endpoint_url}",
|
|
471
|
+
length=max_attempts,
|
|
472
|
+
show_eta=True,
|
|
473
|
+
show_percent=True,
|
|
474
|
+
fill_char="█",
|
|
475
|
+
empty_char="░",
|
|
476
|
+
) as bar:
|
|
477
|
+
for attempt in bar:
|
|
478
|
+
try:
|
|
479
|
+
response = requests.get(endpoint_url, allow_redirects=False, timeout=5)
|
|
480
|
+
if response.status_code < 400: # Consider any non-error response as success
|
|
481
|
+
click.echo(click.style("\n✅ HTTPS endpoint is now accessible!", fg="green", bold=True))
|
|
482
|
+
break
|
|
483
|
+
except requests.RequestException:
|
|
484
|
+
pass
|
|
485
|
+
|
|
486
|
+
if attempt == max_attempts - 1:
|
|
487
|
+
click.echo(
|
|
488
|
+
click.style(
|
|
489
|
+
"\n⚠️ HTTPS endpoint not accessible after 5 minutes", fg="yellow", bold=True
|
|
490
|
+
)
|
|
491
|
+
)
|
|
492
|
+
click.echo(
|
|
493
|
+
" This might be due to DNS propagation or the Load Balancer provisioning delays"
|
|
494
|
+
)
|
|
495
|
+
click.echo(f" Please try accessing {endpoint_url} manually in a few minutes")
|
|
496
|
+
else:
|
|
497
|
+
time.sleep(10)
|
|
498
|
+
|
|
499
|
+
if not skip_apply:
|
|
500
|
+
# Print a summary with the endpoint URL
|
|
501
|
+
click.echo("\n" + "=" * 60)
|
|
502
|
+
click.echo("DEPLOYMENT SUMMARY".center(60))
|
|
503
|
+
click.echo("=" * 60)
|
|
504
|
+
click.echo("✅ Load balancer provisioned")
|
|
505
|
+
|
|
506
|
+
click.echo(f"\n🔗 Tinybird is available at: https://{dns_record}.{dns_zone_name}")
|
|
507
|
+
|
|
508
|
+
click.echo(
|
|
509
|
+
"\n📌 Note: It may take a few minutes for DNS to propagate and the HTTPS certificate to be fully provisioned."
|
|
510
|
+
)
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
@infra.command(name="rm")
|
|
514
|
+
@click.argument("name")
|
|
515
|
+
@click.pass_context
|
|
516
|
+
@coro
|
|
517
|
+
async def infra_rm(ctx: click.Context, name: str):
|
|
518
|
+
"""Delete an infrastructure from Tinybird"""
|
|
519
|
+
try:
|
|
520
|
+
click.echo(FeedbackManager.highlight(message=f"\n» Deleting infrastructure '{name}' from Tinybird..."))
|
|
521
|
+
client: TinyB = ctx.ensure_object(dict)["client"]
|
|
522
|
+
user_workspaces = await client.user_workspaces_with_organization()
|
|
523
|
+
admin_org_id = user_workspaces.get("organization_id")
|
|
524
|
+
if not admin_org_id:
|
|
525
|
+
raise CLIException("No organization associated to this workspace")
|
|
526
|
+
infras = await client.infra_list(admin_org_id)
|
|
527
|
+
infra_id = next((infra["id"] for infra in infras if infra["name"] == name), None)
|
|
528
|
+
if not infra_id:
|
|
529
|
+
raise CLIException(f"Infrastructure '{name}' not found")
|
|
530
|
+
await client.infra_delete(infra_id, admin_org_id)
|
|
531
|
+
click.echo(FeedbackManager.success(message=f"\n✓ Infrastructure '{name}' deleted"))
|
|
532
|
+
except Exception as e:
|
|
533
|
+
click.echo(FeedbackManager.error(message=f"✗ Error: {e}"))
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
@infra.command(name="ls")
|
|
537
|
+
@click.pass_context
|
|
538
|
+
@coro
|
|
539
|
+
async def infra_ls(ctx: click.Context):
|
|
540
|
+
"""List self-managed infrastructures"""
|
|
541
|
+
|
|
542
|
+
client: TinyB = ctx.ensure_object(dict)["client"]
|
|
543
|
+
config = CLIConfig.get_project_config()
|
|
544
|
+
user_client = config.get_client(token=config.get_user_token() or "")
|
|
545
|
+
user_workspaces = await user_client.user_workspaces_with_organization()
|
|
546
|
+
admin_org_id = user_workspaces.get("organization_id")
|
|
547
|
+
infras = await client.infra_list(organization_id=admin_org_id)
|
|
548
|
+
columns = [
|
|
549
|
+
"name",
|
|
550
|
+
"host",
|
|
551
|
+
]
|
|
552
|
+
table_human_readable = []
|
|
553
|
+
table_machine_readable = []
|
|
554
|
+
|
|
555
|
+
for infra in infras:
|
|
556
|
+
name = infra["name"]
|
|
557
|
+
host = infra["host"]
|
|
558
|
+
|
|
559
|
+
table_human_readable.append((name, host))
|
|
560
|
+
table_machine_readable.append({"name": name, "host": host})
|
|
561
|
+
|
|
562
|
+
click.echo(FeedbackManager.info(message="\n** Infras:"))
|
|
563
|
+
echo_safe_humanfriendly_tables_format_smart_table(table_human_readable, column_names=columns)
|
|
564
|
+
click.echo("\n")
|