From dcfd7c76668503006604cc6f2350fc2875562ace Mon Sep 17 00:00:00 2001 From: Gustavo Hidalgo Date: Mon, 5 Feb 2024 09:03:07 -0500 Subject: [PATCH] Use Key Vault secrets instead of cert-manager secret in AKS cluster (#185) * Update deployment script and add nginx-values.yaml * Update Dockerfile, values.yaml, variables.tf, and lib script * Update to satisfy pydantic and mypy * More mypy induced changes * More mypy changes --- deployment/Dockerfile | 2 +- deployment/bin/deploy | 17 +++++----- deployment/bin/lib | 6 +++- deployment/bin/nginx-values.yaml | 16 ++++++++++ deployment/helm/deploy-values.template.yaml | 19 +++++++---- .../templates/cluster_issuer.yaml | 20 ------------ .../pc-apis-ingress/templates/ingress.yaml | 2 +- .../templates/secret-provider.yaml | 28 ++++++++++++++++ deployment/helm/pc-apis-ingress/values.yaml | 9 ++++++ deployment/terraform/resources/aks.tf | 32 +++++++++++++++++++ deployment/terraform/resources/output.tf | 16 ++++++++++ deployment/terraform/resources/providers.tf | 2 ++ deployment/terraform/resources/variables.tf | 12 +++++++ pccommon/pccommon/config/__init__.py | 1 + pccommon/pccommon/logging.py | 2 +- pccommon/pccommon/redis.py | 2 +- pccommon/pccommon/tables.py | 2 +- pccommon/setup.py | 10 ++---- pcstac/Dockerfile.dev | 2 +- pcstac/pcstac/config.py | 14 ++++---- pcstac/requirements.txt | 20 ++++++++---- pcstac/setup.py | 2 +- pctiler/Dockerfile.dev | 2 +- pctiler/pctiler/config.py | 2 +- pctiler/setup.py | 4 ++- requirements-dev.txt | 4 +-- 26 files changed, 179 insertions(+), 69 deletions(-) create mode 100644 deployment/bin/nginx-values.yaml create mode 100644 deployment/helm/pc-apis-ingress/templates/secret-provider.yaml diff --git a/deployment/Dockerfile b/deployment/Dockerfile index d77afd15..31c2d3ec 100644 --- a/deployment/Dockerfile +++ b/deployment/Dockerfile @@ -33,7 +33,7 @@ RUN apt-get install -y kubectl RUN curl https://baltocdn.com/helm/signing.asc | apt-key add - RUN echo "deb https://baltocdn.com/helm/stable/debian/ all main" | tee /etc/apt/sources.list.d/helm-stable-debian.list RUN apt-get update -RUN apt-get install helm=3.5.0-1 +RUN apt-get install helm=3.14.0-1 # Install kubelogin diff --git a/deployment/bin/deploy b/deployment/bin/deploy index 3e3c2038..09714740 100755 --- a/deployment/bin/deploy +++ b/deployment/bin/deploy @@ -138,14 +138,14 @@ if [ "${BASH_SOURCE[0]}" = "${0}" ]; then # Install cert-manager - echo "Installing cert-manager..." + # echo "Installing cert-manager..." - helm upgrade --install \ - cert-manager \ - --namespace pc \ - --create-namespace \ - --version v1.6.0 \ - --set installCRDs=true jetstack/cert-manager + # helm upgrade --install \ + # cert-manager \ + # --namespace pc \ + # --create-namespace \ + # --version v1.6.0 \ + # --set installCRDs=true jetstack/cert-manager echo "===================" echo "==== STAC API =====" @@ -191,7 +191,8 @@ if [ "${BASH_SOURCE[0]}" = "${0}" ]; then --set controller.service.loadBalancerIP="${INGRESS_IP}" \ --set controller.service.annotations."service\.beta\.kubernetes\.io/azure-dns-label-name"="${DNS_LABEL}" \ --wait \ - --timeout 2m0s + --timeout 2m0s \ + -f bin/nginx-values.yaml ######################### # Deploy Azure Function # diff --git a/deployment/bin/lib b/deployment/bin/lib index 458b38f7..4cae1706 100755 --- a/deployment/bin/lib +++ b/deployment/bin/lib @@ -43,6 +43,10 @@ function gather_tf_output() { export ENVIRONMENT=$(tf_output environment) export INGRESS_IP=$(tf_output ingress_ip) export DNS_LABEL=$(tf_output dns_label) + export AZURE_TENANT=$(tf_output tenant_id) + export KEYVAULT_NAME=$(tf_output secret_provider_keyvault_name) + export SECRET_PROVIDER_MANAGED_IDENTITY_ID=$(tf_output secret_provider_managed_identity_id) + export SECRET_PROVIDER_KEYVAULT_SECRET=$(tf_output secret_provider_keyvault_secret) export FUNCTION_APP_NAME=$(tf_output function_app_name) @@ -53,7 +57,7 @@ function gather_tf_output() { function render_values() { echo "Rendering chart value files..." - + bin/jinja ${TF_OUTPUT_FILE} ${TEMPLATE_PATH} ${DEPLOY_VALUES_FILE} } diff --git a/deployment/bin/nginx-values.yaml b/deployment/bin/nginx-values.yaml new file mode 100644 index 00000000..ab3dbc5a --- /dev/null +++ b/deployment/bin/nginx-values.yaml @@ -0,0 +1,16 @@ +controller: + podLabels: + azure.workload.identity/use: "true" + extraVolumes: + - name: secrets-store-inline + csi: + driver: secrets-store.csi.k8s.io + readOnly: true + volumeAttributes: + secretProviderClass: "keyvault" + extraVolumeMounts: + - name: secrets-store-inline + mountPath: "/mnt/secrets-store" + readOnly: true + extraArgs: + default-ssl-certificate: pc/planetarycomputer-test-certificate \ No newline at end of file diff --git a/deployment/helm/deploy-values.template.yaml b/deployment/helm/deploy-values.template.yaml index bf50966c..27ddde23 100644 --- a/deployment/helm/deploy-values.template.yaml +++ b/deployment/helm/deploy-values.template.yaml @@ -73,8 +73,8 @@ tiler: stac_api_url: http://planetary-computer-stac.pc.svc.cluster.local stac_api_href: stac/ - # PCT sas needs to be accessed through api management - pc_sdk_sas_url: https://pct-sas-westeurope-staging-apim.azure-api.net/sas/token + # PCT sas needs to be accessed through Azure Front Door + pc_sdk_sas_url: https://planetarycomputer-test.microsoft.com/sas/token pc_sdk_subscription_key: "{{ tf.pc_sdk_subscription_key }}" vectortile_sa_base_url: https://pcvectortiles.blob.core.windows.net @@ -111,7 +111,7 @@ pcingress: secretName: "pqe-tls-secret" certIssuer: - enabled: true + enabled: false privateKeySecretRef: "{{ tf.cluster_cert_issuer }}" server: "{{ tf.cluster_cert_server }}" issuerEmail: "planetarycomputer@microsoft.com" @@ -119,13 +119,11 @@ pcingress: ingress: enabled: true - tlsHost: "{{ tf.dns_label }}.{{ tf.location }}.cloudapp.azure.com" + tlsHost: "planetarycomputer-test.microsoft.com" hosts: - - "{{ tf.dns_label }}.{{ tf.location }}.cloudapp.azure.com" - "planetarycomputer-test.microsoft.com" annotations: kubernetes.io/ingress.class: nginx - cert-manager.io/cluster-issuer: "{{ tf.cluster_cert_issuer }}-pcingress" nginx.ingress.kubernetes.io/rewrite-target: /$2 nginx.ingress.kubernetes.io/use-regex: "true" nginx.ingress.kubernetes.io/proxy-buffer-size: "16k" @@ -146,3 +144,12 @@ postgres: user: "{{ tf.pg_user }}" password: "{{ tf.pg_password }}" dbName: "{{ tf.pg_database }}" + +secretProvider: + create: true + providerName: "keyvault" + userAssignedIdentityID: "{{ env.SECRET_PROVIDER_MANAGED_IDENTITY_ID }}" + tenantId: "{{ env.AZURE_TENANT }}" + keyvaultName: "{{ env.KEYVAULT_NAME }}" + keyvaultCertificateName: "{{ env.SECRET_PROVIDER_KEYVAULT_SECRET }}" + kubernetesCertificateSecretName: "{{ env.SECRET_PROVIDER_KEYVAULT_SECRET }}" \ No newline at end of file diff --git a/deployment/helm/pc-apis-ingress/templates/cluster_issuer.yaml b/deployment/helm/pc-apis-ingress/templates/cluster_issuer.yaml index 36ef27eb..e69de29b 100644 --- a/deployment/helm/pc-apis-ingress/templates/cluster_issuer.yaml +++ b/deployment/helm/pc-apis-ingress/templates/cluster_issuer.yaml @@ -1,20 +0,0 @@ -{{- if .Values.pcingress.certIssuer.enabled -}} -apiVersion: cert-manager.io/v1 -kind: ClusterIssuer -metadata: - name: {{ .Values.pcingress.certIssuer.privateKeySecretRef }}-pcingress -spec: - acme: - server: {{ .Values.pcingress.certIssuer.server }} - email: {{ .Values.pcingress.certIssuer.issuerEmail }} - privateKeySecretRef: - name: {{ .Values.pcingress.certIssuer.privateKeySecretRef }} - solvers: - - http01: - ingress: - class: nginx - podTemplate: - spec: - nodeSelector: - "kubernetes.io/os": linux -{{- end }} \ No newline at end of file diff --git a/deployment/helm/pc-apis-ingress/templates/ingress.yaml b/deployment/helm/pc-apis-ingress/templates/ingress.yaml index 15718cf3..0e814f8d 100644 --- a/deployment/helm/pc-apis-ingress/templates/ingress.yaml +++ b/deployment/helm/pc-apis-ingress/templates/ingress.yaml @@ -18,7 +18,7 @@ spec: tls: - hosts: - {{ .Values.pcingress.ingress.tlsHost }} - secretName: {{ .Values.pcingress.cert.secretName }} + secretName: {{ .Values.secretProvider.kubernetesCertificateSecretName }} rules: {{- range .Values.pcingress.ingress.hosts }} - host: {{ . }} diff --git a/deployment/helm/pc-apis-ingress/templates/secret-provider.yaml b/deployment/helm/pc-apis-ingress/templates/secret-provider.yaml new file mode 100644 index 00000000..016800ec --- /dev/null +++ b/deployment/helm/pc-apis-ingress/templates/secret-provider.yaml @@ -0,0 +1,28 @@ +{{- if .Values.secretProvider.create -}} +apiVersion: secrets-store.csi.x-k8s.io/v1 +kind: SecretProviderClass +metadata: + name: {{ .Values.secretProvider.providerName }} + namespace: {{ .Values.namespace }} +spec: + provider: azure + secretObjects: + - secretName: {{ .Values.secretProvider.kubernetesCertificateSecretName }} + type: kubernetes.io/tls + data: + - objectName: {{ .Values.secretProvider.keyvaultCertificateName }} + key: tls.crt + - objectName: {{ .Values.secretProvider.keyvaultCertificateName }} + key: tls.key + parameters: + usePodIdentity: "false" + clientID: "{{ .Values.secretProvider.userAssignedIdentityID }}" + keyvaultName: "{{ .Values.secretProvider.keyvaultName }}" + tenantId: "{{ .Values.secretProvider.tenantId }}" + cloudName: "" + objects: | + array: + - | + objectName: {{ .Values.secretProvider.keyvaultCertificateName }} + objectType: secret +{{- end }} \ No newline at end of file diff --git a/deployment/helm/pc-apis-ingress/values.yaml b/deployment/helm/pc-apis-ingress/values.yaml index af268956..4bf79c17 100644 --- a/deployment/helm/pc-apis-ingress/values.yaml +++ b/deployment/helm/pc-apis-ingress/values.yaml @@ -7,6 +7,15 @@ stac: tiler: enabled: true +secretProvider: + create: true + providerName: "keyvault" + userAssignedIdentityID: "" + tenantId: "" + keyvaultName: "" + keyvaultCertificateName: "" + kubernetesCertificateSecretName: "" + pcingress: services: stac: diff --git a/deployment/terraform/resources/aks.tf b/deployment/terraform/resources/aks.tf index 4f8ffae5..c283540e 100644 --- a/deployment/terraform/resources/aks.tf +++ b/deployment/terraform/resources/aks.tf @@ -5,6 +5,19 @@ resource "azurerm_kubernetes_cluster" "pc" { dns_prefix = "${local.prefix}-cluster" kubernetes_version = var.k8s_version + key_vault_secrets_provider { + secret_rotation_enabled = true + } + oidc_issuer_enabled = true + + # https://learn.microsoft.com/en-us/azure/aks/auto-upgrade-cluster#use-cluster-auto-upgrade + automatic_channel_upgrade = "rapid" + + # https://learn.microsoft.com/en-us/azure/aks/auto-upgrade-node-os-image + node_os_channel_upgrade = "NodeImage" + + image_cleaner_enabled = true + default_node_pool { name = "agentpool" os_sku = "AzureLinux" @@ -35,3 +48,22 @@ resource "azurerm_role_assignment" "network" { role_definition_name = "Network Contributor" principal_id = azurerm_kubernetes_cluster.pc.identity[0].principal_id } + +resource "azurerm_federated_identity_credential" "cluster" { + name = "federated-id-${local.prefix}-${var.environment}" + resource_group_name = azurerm_kubernetes_cluster.pc.node_resource_group + audience = ["api://AzureADTokenExchange"] + issuer = azurerm_kubernetes_cluster.pc.oidc_issuer_url + subject = "system:serviceaccount:pc:nginx-ingress-ingress-nginx" + parent_id = "/subscriptions/a84a690d-585b-4c7c-80d9-851a48af5a50/resourceGroups/MC_pct-apis-westeurope-staging_rg_pct-apis-westeurope-staging-cluster_westeurope/providers/Microsoft.ManagedIdentity/userAssignedIdentities/azurekeyvaultsecretsprovider-pct-apis-westeurope-staging-cluster" + timeouts {} +} + +# If you add a second azurerm provider and use a data block to reference this key vault +# then the identity that deploys this has to have permissions over both subscriptions +# This role assignment was created manually but the resource is left here as a reminder +# resource "azurerm_role_assignment" "certificateAccess" { +# scope = #REDACTED +# role_definition_name = #REDACTED +# principal_id = azurerm_kubernetes_cluster.pc.key_vault_secrets_provider[0].secret_identity[0].object_id +# } diff --git a/deployment/terraform/resources/output.tf b/deployment/terraform/resources/output.tf index 9bbdd79c..f3715910 100644 --- a/deployment/terraform/resources/output.tf +++ b/deployment/terraform/resources/output.tf @@ -14,6 +14,10 @@ output "resource_group" { value = azurerm_resource_group.pc.name } +output "tenant_id" { + value = data.azurerm_client_config.current.tenant_id +} + # -- Postgres output "pg_host" { @@ -57,6 +61,18 @@ output "dns_label" { value = azurerm_public_ip.pc.domain_name_label } +output "secret_provider_keyvault_name" { + value = var.secret_provider_keyvault_name +} + +output "secret_provider_managed_identity_id" { + value = azurerm_kubernetes_cluster.pc.key_vault_secrets_provider[0].secret_identity[0].client_id +} + +output "secret_provider_keyvault_secret" { + value = var.secret_provider_keyvault_secret +} + ## STAC API output "stac_replica_count" { diff --git a/deployment/terraform/resources/providers.tf b/deployment/terraform/resources/providers.tf index 4686fbb5..0c06d28c 100644 --- a/deployment/terraform/resources/providers.tf +++ b/deployment/terraform/resources/providers.tf @@ -12,3 +12,5 @@ terraform { } } } + +data "azurerm_client_config" "current" {} \ No newline at end of file diff --git a/deployment/terraform/resources/variables.tf b/deployment/terraform/resources/variables.tf index 4ef37848..82e609d0 100644 --- a/deployment/terraform/resources/variables.tf +++ b/deployment/terraform/resources/variables.tf @@ -84,6 +84,18 @@ variable "pc_sdk_subscription_key_secret_name" { default = "pct-tiler-sdk-subscription-key" } +variable "secret_provider_keyvault_name" { + type = string + description = "The name of the KeyVault that holds the secrets" + default = "pc-deploy-secrets" +} + +variable "secret_provider_keyvault_secret" { + type = string + description = "The name of the certificate in the KeyVault for TLS ingress" + default = "planetarycomputer-test-certificate" +} + # -- Functions -- variable "output_storage_account_name" { diff --git a/pccommon/pccommon/config/__init__.py b/pccommon/pccommon/config/__init__.py index ed35b4cf..487c48e2 100644 --- a/pccommon/pccommon/config/__init__.py +++ b/pccommon/pccommon/config/__init__.py @@ -24,4 +24,5 @@ def get_all_render_configs() -> Dict[str, DefaultRenderConfig]: for id, coll in get_apis_config() .get_collection_config_table() .get_all_configs() + if id is not None } diff --git a/pccommon/pccommon/logging.py b/pccommon/pccommon/logging.py index 77e7d2df..1b7e6e2b 100644 --- a/pccommon/pccommon/logging.py +++ b/pccommon/pccommon/logging.py @@ -61,7 +61,7 @@ def filter(self, record: logging.LogRecord) -> bool: # Prevent successful health check pings from being logged class HealthCheckFilter(logging.Filter): def filter(self, record: logging.LogRecord) -> bool: - if len(record.args) != 5: + if record.args is not None and len(record.args) != 5: return True args = cast(Tuple[str, str, str, str, int], record.args) diff --git a/pccommon/pccommon/redis.py b/pccommon/pccommon/redis.py index 4f9f73bb..b3ac0fcf 100644 --- a/pccommon/pccommon/redis.py +++ b/pccommon/pccommon/redis.py @@ -114,7 +114,7 @@ async def cached_result( try: r = request.app.state.redis if r: - cached: str = await r.get(host_cache_key) + cached = await r.get(host_cache_key) if cached: logger.info( "Cache result hit", diff --git a/pccommon/pccommon/tables.py b/pccommon/pccommon/tables.py index 20993306..af60b7b1 100644 --- a/pccommon/pccommon/tables.py +++ b/pccommon/pccommon/tables.py @@ -222,7 +222,7 @@ def get_all(self) -> Iterable[Tuple[Optional[str], Optional[str], M]]: yield ( partition_key, row_key, - self._parse_model(entity, partition_key, row_key), + self._parse_model(entity, partition_key, row_key), # type: ignore ) diff --git a/pccommon/setup.py b/pccommon/setup.py index 4a7db2e6..d330ecba 100644 --- a/pccommon/setup.py +++ b/pccommon/setup.py @@ -21,14 +21,8 @@ ] extra_reqs = { - "test": [ - "pytest", - "pytest-asyncio", - ], - "dev": [ - "pytest", - "pytest-asyncio", - ], + "test": ["pytest", "pytest-asyncio", "types-redis"], + "dev": ["pytest", "pytest-asyncio", "types-redis"], } setup( diff --git a/pcstac/Dockerfile.dev b/pcstac/Dockerfile.dev index 127fe2ec..b61d44a3 100644 --- a/pcstac/Dockerfile.dev +++ b/pcstac/Dockerfile.dev @@ -3,4 +3,4 @@ FROM pc-apis-stac COPY requirements-dev.txt requirements-dev.txt RUN pip install -r requirements-dev.txt -RUN pip install -e ./pccommon -e ./pcstac +RUN pip install -e ./pccommon[dev] -e ./pcstac diff --git a/pcstac/pcstac/config.py b/pcstac/pcstac/config.py index 7bceda91..851c9ebb 100644 --- a/pcstac/pcstac/config.py +++ b/pcstac/pcstac/config.py @@ -64,11 +64,11 @@ class BackPressureConfig(BaseModel): class BackPressures(BaseSettings): - collections: BackPressureConfig - collection: BackPressureConfig - item: BackPressureConfig - items: BackPressureConfig - search: BackPressureConfig + collections: BackPressureConfig = BackPressureConfig() + collection: BackPressureConfig = BackPressureConfig() + item: BackPressureConfig = BackPressureConfig() + items: BackPressureConfig = BackPressureConfig() + search: BackPressureConfig = BackPressureConfig() class Settings(BaseSettings): @@ -96,8 +96,8 @@ class Settings(BaseSettings): db_min_conn_size: int = Field(env=DB_MIN_CONN_ENV_VAR, default=1) openapi_url: str = "/openapi.json" api_version: str = f"v{API_VERSION}" - rate_limits: RateLimits - back_pressures: BackPressures + rate_limits: RateLimits = RateLimits() + back_pressures: BackPressures = BackPressures() request_timout: int = Field(env=REQUEST_TIMEOUT_ENV_VAR, default=30) def get_tiler_href(self, request: Request) -> str: diff --git a/pcstac/requirements.txt b/pcstac/requirements.txt index 667001e9..9e72c0dc 100644 --- a/pcstac/requirements.txt +++ b/pcstac/requirements.txt @@ -1,8 +1,8 @@ # -# This file is autogenerated by pip-compile with Python 3.9 +# This file is autogenerated by pip-compile with Python 3.10 # by the following command: # -# pip-compile --extra=server --output-file=pcstac/requirements.txt --resolver=backtracking pccommon/setup.py pcstac/setup.py +# pip-compile --extra=server --output-file=pcstac/requirements.txt pccommon/setup.py pcstac/setup.py # anyio==3.7.0 # via @@ -143,7 +143,9 @@ protobuf==4.23.2 psutil==5.9.5 # via opencensus-ext-azure psycopg[binary]==3.1.9 - # via pypgstac + # via + # psycopg + # pypgstac psycopg-binary==3.1.9 # via psycopg psycopg-pool==3.1.7 @@ -174,12 +176,15 @@ pygeoif==1.0.0 pyhumps==3.5.3 # via pccommon (pccommon/setup.py) pyjwt[crypto]==2.7.0 - # via msal + # via + # msal + # pyjwt pypgstac[psycopg]==0.7.9 # via # pcstac (pcstac/setup.py) + # pypgstac # stac-fastapi-pgstac -pystac==1.7.3 +pystac==1.9.0 # via # pcstac (pcstac/setup.py) # stac-fastapi-types @@ -269,7 +274,6 @@ typing-extensions==4.6.3 # pydantic # pygeoif # redis - # starlette tzlocal==5.0.1 # via dateparser urllib3==1.26.16 @@ -277,7 +281,9 @@ urllib3==1.26.16 # google-auth # requests uvicorn[standard]==0.17.6 - # via pcstac (pcstac/setup.py) + # via + # pcstac (pcstac/setup.py) + # uvicorn uvloop==0.17.0 # via uvicorn version-parser==1.0.1 diff --git a/pcstac/setup.py b/pcstac/setup.py index 1dd088e4..3e04d0ea 100644 --- a/pcstac/setup.py +++ b/pcstac/setup.py @@ -10,7 +10,7 @@ "stac-fastapi.types==2.4.8", # Required due to some imports related to pypgstac CLI usage in startup script "pypgstac[psycopg]~=0.7", - "pystac==1.*", + "pystac>=1.9", ] extra_reqs = { diff --git a/pctiler/Dockerfile.dev b/pctiler/Dockerfile.dev index b427cb79..88491ca4 100644 --- a/pctiler/Dockerfile.dev +++ b/pctiler/Dockerfile.dev @@ -3,4 +3,4 @@ FROM pc-apis-tiler COPY requirements-dev.txt requirements-dev.txt RUN python3 -m pip install -r requirements-dev.txt -RUN python3 -m pip install -e ./pccommon -e ./pctiler +RUN python3 -m pip install -e ./pccommon -e ./pctiler[dev] diff --git a/pctiler/pctiler/config.py b/pctiler/pctiler/config.py index d278758e..265112f0 100644 --- a/pctiler/pctiler/config.py +++ b/pctiler/pctiler/config.py @@ -38,7 +38,7 @@ class Settings(BaseSettings): mosaic_endpoint_prefix: str = "/mosaic" legend_endpoint_prefix: str = "/legend" vector_tile_endpoint_prefix: str = "/vector" - vector_tile_sa_base_url: str = Field(env=VECTORTILE_SA_BASE_URL_ENV_VAR) + vector_tile_sa_base_url: str = Field(env=VECTORTILE_SA_BASE_URL_ENV_VAR, default="") debug: bool = os.getenv("TILER_DEBUG", "False").lower() == "true" api_version: str = "1.0" diff --git a/pctiler/setup.py b/pctiler/setup.py index 8ccbc79b..7fe371a8 100644 --- a/pctiler/setup.py +++ b/pctiler/setup.py @@ -1,11 +1,13 @@ """Setup for pctiler.""" +from typing import List from setuptools import find_packages, setup # Runtime requirements, see environment.yaml -inst_reqs = [] +inst_reqs: List[str] = [] extra_reqs = { + "dev": ["types-requests"], "server": [ "uvicorn[standard]>=0.17.0,<0.18.0", ], diff --git a/requirements-dev.txt b/requirements-dev.txt index dc30db33..2c8fd003 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,7 +1,7 @@ black==22.3.0 flake8==3.8.4 isort==5.9.2 -mypy==0.800 +mypy==1.8.0 openapi-spec-validator==0.3.0 cachetools<=4.2. pytest==7.* @@ -9,5 +9,5 @@ pytest-asyncio==0.18.* httpx>=0.22.0 # Mypy types - +types-python-dateutil types-cachetools