Refactor monorepo build-system

Introduce a per-charm configuration file `.sunbeam-build.yaml`,
containing information about needed external libraries, internal
libraries, and configuration templates.

Introduce new management script `repository.py`, responsible for
preparing, cleaning and updating libs.

Move all internal libraries back to their original charms. Each charm is
responsible of its own libraries.

Change-Id: I9edabed1c252cae60fcd945b931952aeaef12481
Signed-off-by: Guillaume Boutry <guillaume.boutry@canonical.com>
This commit is contained in:
Guillaume Boutry 2024-07-08 12:36:57 +02:00
parent f3ab145e58
commit 16a65cf4e4
No known key found for this signature in database
GPG Key ID: E95E3326872E55DE
59 changed files with 739 additions and 894 deletions

View File

@ -0,0 +1,17 @@
external-libraries:
- charms.data_platform_libs.v0.data_interfaces
- charms.rabbitmq_k8s.v0.rabbitmq
- charms.traefik_k8s.v2.ingress
- charms.certificate_transfer_interface.v0.certificate_transfer
- charms.loki_k8s.v1.loki_push_api
internal-libraries:
- charms.keystone_k8s.v1.identity_service
templates:
- parts/section-database
- parts/database-connection
- parts/database-connection-settings
- parts/section-identity
- parts/identity-data
- parts/section-oslo-messaging-rabbit
- parts/section-service-credentials
- ca-bundle.pem.j2

View File

@ -0,0 +1,19 @@
external-libraries:
- charms.data_platform_libs.v0.data_interfaces
- charms.rabbitmq_k8s.v0.rabbitmq
- charms.traefik_k8s.v2.ingress
- charms.vault_k8s.v0.vault_kv
- charms.certificate_transfer_interface.v0.certificate_transfer
- charms.loki_k8s.v1.loki_push_api
internal-libraries:
- charms.keystone_k8s.v1.identity_service
- charms.keystone_k8s.v0.identity_resource
templates:
- parts/section-database
- parts/database-connection
- parts/database-connection-settings
- parts/section-identity
- parts/identity-data
- parts/section-oslo-messaging-rabbit
- parts/section-service-user
- ca-bundle.pem.j2

1
charms/ceilometer-k8s/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
!lib/charms/ceilometer_k8s

View File

@ -0,0 +1,12 @@
external-libraries:
- charms.rabbitmq_k8s.v0.rabbitmq
- charms.certificate_transfer_interface.v0.certificate_transfer
- charms.loki_k8s.v1.loki_push_api
internal-libraries:
- charms.keystone_k8s.v0.identity_credentials
- charms.gnocchi_k8s.v0.gnocchi_service
templates:
- parts/identity-data-id-creds
- parts/section-oslo-messaging-rabbit
- parts/section-service-credentials-from-identity-service
- ca-bundle.pem.j2

1
charms/cinder-ceph-k8s/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
!lib/charms/cinder_ceph_k8s

View File

@ -0,0 +1,14 @@
external-libraries:
- charms.data_platform_libs.v0.data_interfaces
- charms.rabbitmq_k8s.v0.rabbitmq
- charms.traefik_k8s.v2.ingress
- charms.loki_k8s.v1.loki_push_api
internal-libraries:
- charms.keystone_k8s.v0.identity_credentials
- charms.cinder_k8s.v0.storage_backend
templates:
- parts/section-database
- parts/database-connection
- parts/database-connection-settings
- parts/section-oslo-messaging-rabbit
- parts/section-oslo-notifications

1
charms/cinder-k8s/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
!lib/charms/cinder_k8s

View File

@ -0,0 +1,17 @@
external-libraries:
- charms.data_platform_libs.v0.data_interfaces
- charms.rabbitmq_k8s.v0.rabbitmq
- charms.traefik_k8s.v2.ingress
- charms.certificate_transfer_interface.v0.certificate_transfer
- charms.loki_k8s.v1.loki_push_api
internal-libraries:
- charms.keystone_k8s.v1.identity_service
templates:
- parts/section-database
- parts/database-connection
- parts/database-connection-settings
- parts/section-identity
- parts/identity-data
- parts/section-oslo-messaging-rabbit
- parts/section-service-user
- ca-bundle.pem.j2

1
charms/designate-bind-k8s/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
!lib/charms/designate_bind_k8s

View File

@ -0,0 +1,3 @@
external-libraries:
- charms.observability_libs.v1.kubernetes_service_patch
- charms.loki_k8s.v1.loki_push_api

1
charms/designate-k8s/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
!lib/charms/designate_k8s

View File

@ -0,0 +1,17 @@
external-libraries:
- charms.data_platform_libs.v0.data_interfaces
- charms.rabbitmq_k8s.v0.rabbitmq
- charms.traefik_k8s.v2.ingress
- charms.certificate_transfer_interface.v0.certificate_transfer
- charms.loki_k8s.v1.loki_push_api
internal-libraries:
- charms.keystone_k8s.v1.identity_service
- charms.designate_bind_k8s.v0.bind_rndc
templates:
- parts/database-connection
- parts/database-connection-settings
- parts/section-identity
- parts/identity-data
- parts/section-oslo-messaging-rabbit
- parts/section-service-user
- ca-bundle.pem.j2

View File

@ -0,0 +1,18 @@
external-libraries:
- charms.data_platform_libs.v0.data_interfaces
- charms.rabbitmq_k8s.v0.rabbitmq
- charms.traefik_k8s.v2.ingress
- charms.certificate_transfer_interface.v0.certificate_transfer
- charms.loki_k8s.v1.loki_push_api
internal-libraries:
- charms.keystone_k8s.v1.identity_service
templates:
- parts/section-database
- parts/database-connection
- parts/database-connection-settings
- parts/section-identity
- parts/identity-data
- parts/section-oslo-messaging-rabbit
- parts/section-oslo-notifications
- parts/section-service-user
- ca-bundle.pem.j2

1
charms/gnocchi-k8s/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
!lib/charms/gnocchi_k8s

View File

@ -0,0 +1,13 @@
external-libraries:
- charms.data_platform_libs.v0.data_interfaces
- charms.rabbitmq_k8s.v0.rabbitmq
- charms.traefik_k8s.v2.ingress
- charms.certificate_transfer_interface.v0.certificate_transfer
- charms.loki_k8s.v1.loki_push_api
internal-libraries:
- charms.keystone_k8s.v1.identity_service
templates:
- parts/database-connection
- parts/section-identity
- parts/identity-data
- ca-bundle.pem.j2

View File

@ -0,0 +1,18 @@
external-libraries:
- charms.data_platform_libs.v0.data_interfaces
- charms.rabbitmq_k8s.v0.rabbitmq
- charms.traefik_route_k8s.v0.traefik_route
- charms.certificate_transfer_interface.v0.certificate_transfer
- charms.loki_k8s.v1.loki_push_api
internal-libraries:
- charms.keystone_k8s.v1.identity_service
- charms.keystone_k8s.v0.identity_resource
templates:
- parts/section-database
- parts/database-connection
- parts/database-connection-settings
- parts/section-identity
- parts/identity-data
- parts/section-trustee
- parts/section-oslo-messaging-rabbit
- ca-bundle.pem.j2

View File

@ -0,0 +1,10 @@
external-libraries:
- charms.data_platform_libs.v0.data_interfaces
- charms.rabbitmq_k8s.v0.rabbitmq
- charms.traefik_k8s.v2.ingress
- charms.certificate_transfer_interface.v0.certificate_transfer
- charms.loki_k8s.v1.loki_push_api
internal-libraries:
- charms.keystone_k8s.v0.identity_credentials
templates:
- ca-bundle.pem.j2

1
charms/keystone-k8s/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
!lib/charms/keystone_k8s

View File

@ -0,0 +1,17 @@
external-libraries:
- charms.data_platform_libs.v0.data_interfaces
- charms.rabbitmq_k8s.v0.rabbitmq
- charms.traefik_k8s.v2.ingress
- charms.certificate_transfer_interface.v0.certificate_transfer
- charms.loki_k8s.v1.loki_push_api
templates:
- parts/section-database
- parts/database-connection
- parts/database-connection-settings
- parts/section-federation
- parts/section-middleware
- parts/section-oslo-cache
- parts/section-oslo-messaging-rabbit
- parts/section-oslo-middleware
- parts/section-oslo-notifications
- parts/section-signing

View File

@ -0,0 +1,4 @@
external-libraries:
- charms.loki_k8s.v1.loki_push_api
internal-libraries:
- charms.keystone_k8s.v0.domain_config

View File

@ -0,0 +1,19 @@
external-libraries:
- charms.data_platform_libs.v0.data_interfaces
- charms.rabbitmq_k8s.v0.rabbitmq
- charms.traefik_k8s.v2.ingress
- charms.certificate_transfer_interface.v0.certificate_transfer
- charms.loki_k8s.v1.loki_push_api
internal-libraries:
- charms.keystone_k8s.v1.identity_service
- charms.keystone_k8s.v0.identity_resource
templates:
- parts/section-database
- parts/database-connection
- parts/database-connection-settings
- parts/section-identity
- parts/identity-data
- parts/section-oslo-messaging-rabbit
- parts/section-service-user
- parts/section-trust
- ca-bundle.pem.j2

View File

@ -0,0 +1,20 @@
external-libraries:
- charms.data_platform_libs.v0.data_interfaces
- charms.rabbitmq_k8s.v0.rabbitmq
- charms.traefik_k8s.v2.ingress
- charms.tls_certificates_interface.v3.tls_certificates
- charms.certificate_transfer_interface.v0.certificate_transfer
- charms.loki_k8s.v1.loki_push_api
internal-libraries:
- charms.keystone_k8s.v1.identity_service
- charms.ovn_central_k8s.v0.ovsdb
- charms.designate_k8s.v0.designate_service
templates:
- parts/section-database
- parts/database-connection
- parts/database-connection-settings
- parts/section-identity
- parts/identity-data
- parts/section-oslo-messaging-rabbit
- parts/section-service-user
- ca-bundle.pem.j2

1
charms/nova-k8s/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
!lib/charms/nova_k8s

View File

@ -0,0 +1,19 @@
external-libraries:
- charms.data_platform_libs.v0.data_interfaces
- charms.rabbitmq_k8s.v0.rabbitmq
- charms.traefik_k8s.v2.ingress
- charms.traefik_route_k8s.v0.traefik_route
- charms.certificate_transfer_interface.v0.certificate_transfer
- charms.loki_k8s.v1.loki_push_api
- charms.sunbeam_nova_compute_operator.v0.cloud_compute
internal-libraries:
- charms.keystone_k8s.v1.identity_service
templates:
- parts/section-database
- parts/database-connection
- parts/database-connection-settings
- parts/section-identity
- parts/identity-data
- parts/section-oslo-messaging-rabbit
- parts/section-service-user
- ca-bundle.pem.j2

View File

@ -0,0 +1,18 @@
external-libraries:
- charms.data_platform_libs.v0.data_interfaces
- charms.traefik_k8s.v2.ingress
- charms.tls_certificates_interface.v3.tls_certificates
- charms.certificate_transfer_interface.v0.certificate_transfer
- charms.loki_k8s.v1.loki_push_api
internal-libraries:
- charms.keystone_k8s.v1.identity_service
- charms.keystone_k8s.v0.identity_resource
- charms.ovn_central_k8s.v0.ovsdb
- charms.designate_k8s.v0.designate_service
templates:
- parts/section-database
- parts/database-connection
- parts/database-connection-settings
- parts/section-identity
- parts/identity-data
- ca-bundle.pem.j2

View File

@ -0,0 +1,10 @@
external-libraries:
- charms.grafana_k8s.v0.grafana_dashboard
- charms.prometheus_k8s.v0.prometheus_scrape
- charms.tls_certificates_interface.v3.tls_certificates
- charms.certificate_transfer_interface.v0.certificate_transfer
- charms.loki_k8s.v1.loki_push_api
internal-libraries:
- charms.keystone_k8s.v0.identity_resource
templates:
- ca-bundle.pem.j2

View File

@ -0,0 +1,15 @@
external-libraries:
- charms.data_platform_libs.v0.data_interfaces
- charms.grafana_agent.v0.cos_agent
- charms.observability_libs.v1.kubernetes_service_patch
- charms.operator_libs_linux.v2.snap
- charms.rabbitmq_k8s.v0.rabbitmq
- charms.traefik_k8s.v2.ingress
- charms.tls_certificates_interface.v3.tls_certificates
- charms.certificate_transfer_interface.v0.certificate_transfer
internal-libraries:
- charms.keystone_k8s.v0.identity_credentials
- charms.ovn_central_k8s.v0.ovsdb
- charms.cinder_ceph_k8s.v0.ceph_access
- charms.ceilometer_k8s.v0.ceilometer_service
- charms.nova_k8s.v0.nova_service

View File

@ -0,0 +1,5 @@
external-libraries:
- charms.traefik_k8s.v2.ingress
- charms.loki_k8s.v1.loki_push_api
internal-libraries:
- charms.keystone_k8s.v1.identity_service

1
charms/ovn-central-k8s/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
!lib/charms/ovn_central_k8s

View File

@ -0,0 +1,3 @@
external-libraries:
- charms.tls_certificates_interface.v3.tls_certificates
- charms.loki_k8s.v1.loki_push_api

View File

@ -0,0 +1,6 @@
external-libraries:
- charms.tls_certificates_interface.v3.tls_certificates
- charms.observability_libs.v1.kubernetes_service_patch
- charms.loki_k8s.v1.loki_push_api
internal-libraries:
- charms.ovn_central_k8s.v0.ovsdb

View File

@ -0,0 +1,15 @@
external-libraries:
- charms.data_platform_libs.v0.data_interfaces
- charms.rabbitmq_k8s.v0.rabbitmq
- charms.traefik_k8s.v2.ingress
- charms.certificate_transfer_interface.v0.certificate_transfer
- charms.loki_k8s.v1.loki_push_api
internal-libraries:
- charms.keystone_k8s.v1.identity_service
templates:
- parts/database-connection
- parts/database-connection-settings
- parts/section-identity
- parts/identity-data
- parts/section-service-user
- ca-bundle.pem.j2

View File

@ -0,0 +1,3 @@
external-libraries:
- charms.operator_libs_linux.v2.snap
- charms.tls_certificates_interface.v3.tls_certificates

View File

@ -0,0 +1,2 @@
external-libraries:
- charms.operator_libs_linux.v0.sysctl

View File

@ -0,0 +1,6 @@
external-libraries:
- charms.observability_libs.v1.kubernetes_service_patch
- charms.grafana_k8s.v0.grafana_dashboard
- charms.loki_k8s.v1.loki_push_api
internal-libraries:
- charms.keystone_k8s.v0.identity_resource

549
common.sh
View File

@ -1,549 +0,0 @@
#!/bin/bash
# All libraries required by sunbeam charms are centrally
# maintained in libs folder. The libraries created by
# sunbeam charms are placed in libs/internal and the
# libraries provided by external charms are maintained
# in libs/external.
# All generic template parts are maintained in
# templates/parts folder.
#
# This script provides functions for each sunbeam charms
# all the common files that should be copied to charm
# for building the charm and function testing.
NULL_ARRAY=()
# Internal libs for component. If libs are repeated, reuse the existing component
INTERNAL_CEILOMETER_LIBS=(
"keystone_k8s"
"ceilometer_k8s"
"gnocchi_k8s"
)
INTERNAL_CINDER_LIBS=(
"keystone_k8s"
"cinder_k8s"
)
INTERNAL_CINDER_CEPH_LIBS=(
"keystone_k8s"
"cinder_k8s"
"cinder_ceph_k8s"
)
INTERNAL_DESIGNATE_LIBS=(
"keystone_k8s"
"designate_bind_k8s"
"designate_k8s"
)
INTERNAL_DESIGNATE_BIND_LIBS=(
"designate_bind_k8s"
)
INTERNAL_GNOCCHI_LIBS=(
"keystone_k8s"
"gnocchi_k8s"
)
INTERNAL_KEYSTONE_LIBS=(
"keystone_k8s"
)
INTERNAL_NEUTRON_LIBS=(
"keystone_k8s"
"ovn_central_k8s"
"designate_k8s"
)
INTERNAL_NOVA_LIBS=(
"keystone_k8s"
"nova_k8s"
"sunbeam_nova_compute_operator"
)
INTERNAL_OPENSTACK_HYPERVISOR_LIBS=(
"keystone_k8s"
"ovn_central_k8s"
"cinder_ceph_k8s"
"ceilometer_k8s"
"nova_k8s"
)
INTERNAL_OPENSTACK_IMAGES_SYNC_LIBS=(
"keystone_k8s"
)
INTERNAL_OVN_CENTRAL_LIBS=(
"ovn_central_k8s"
)
# External libs for component. If libs are repeated, reuse the existing component
EXTERNAL_AODH_LIBS=(
"data_platform_libs"
"rabbitmq_k8s"
"traefik_k8s"
"certificate_transfer_interface"
"loki_k8s"
)
EXTERNAL_BARBICAN_LIBS=(
"data_platform_libs"
"rabbitmq_k8s"
"traefik_k8s"
"vault_k8s"
"certificate_transfer_interface"
"loki_k8s"
)
EXTERNAL_CEILOMETER_LIBS=(
"rabbitmq_k8s"
"certificate_transfer_interface"
"loki_k8s"
)
EXTERNAL_CINDER_CEPH_LIBS=(
"data_platform_libs"
"rabbitmq_k8s"
"traefik_k8s"
"loki_k8s"
)
EXTERNAL_DESIGNATE_BIND_LIBS=(
"observability_libs"
"loki_k8s"
)
EXTERNAL_HEAT_LIBS=(
"data_platform_libs"
"rabbitmq_k8s"
"traefik_route_k8s"
"certificate_transfer_interface"
"loki_k8s"
)
EXTERNAL_NEUTRON_LIBS=(
"data_platform_libs"
"rabbitmq_k8s"
"traefik_k8s"
"tls_certificates_interface"
"certificate_transfer_interface"
"loki_k8s"
)
EXTERNAL_NOVA_LIBS=(
"data_platform_libs"
"rabbitmq_k8s"
"traefik_k8s"
"traefik_route_k8s"
"certificate_transfer_interface"
"loki_k8s"
)
EXTERNAL_OCTAVIA_LIBS=(
"data_platform_libs"
"traefik_k8s"
"tls_certificates_interface"
"certificate_transfer_interface"
"loki_k8s"
)
EXTERNAL_OPENSTACK_EXPORTER_LIBS=(
"grafana_k8s"
"prometheus_k8s"
"tls_certificates_interface"
"certificate_transfer_interface"
"loki_k8s"
)
EXTERNAL_OPENSTACK_HYPERVISOR_LIBS=(
"data_platform_libs"
"grafana_agent"
"observability_libs"
"operator_libs_linux"
"rabbitmq_k8s"
"traefik_k8s"
"tls_certificates_interface"
"certificate_transfer_interface"
)
EXTERNAL_OPENSTACK_IMAGES_SYNC_LIBS=(
"traefik_k8s"
"loki_k8s"
)
EXTERNAL_SUNBEAM_CLUSTERD_LIBS=(
"operator_libs_linux"
"tls_certificates_interface"
)
EXTERNAL_SUNBEAM_MACHINE_LIBS=(
"operator_libs_linux"
)
EXTERNAL_OVN_CENTRAL_LIBS=(
"tls_certificates_interface"
"loki_k8s"
)
EXTERNAL_OVN_RELAY_LIBS=(
"tls_certificates_interface"
"observability_libs"
"loki_k8s"
)
EXTERNAL_TEMPEST_LIBS=(
"observability_libs"
"grafana_k8s"
"loki_k8s"
)
# Config template parts for each component.
CONFIG_TEMPLATES_AODH=(
"parts/section-database"
"parts/database-connection"
"parts/database-connection-settings"
"parts/section-identity"
"parts/identity-data"
"parts/section-oslo-messaging-rabbit"
"parts/section-service-credentials"
"ca-bundle.pem.j2"
)
CONFIG_TEMPLATES_BARBICAN=(
"parts/section-database"
"parts/database-connection"
"parts/database-connection-settings"
"parts/section-identity"
"parts/identity-data"
"parts/section-oslo-messaging-rabbit"
"parts/section-service-user"
"ca-bundle.pem.j2"
)
CONFIG_TEMPLATES_CEILOMETER=(
"parts/identity-data-id-creds"
"parts/section-oslo-messaging-rabbit"
"parts/section-service-credentials-from-identity-service"
"ca-bundle.pem.j2"
)
CONFIG_TEMPLATES_CINDER=(
"parts/section-database"
"parts/database-connection"
"parts/database-connection-settings"
"parts/section-identity"
"parts/identity-data"
"parts/section-oslo-messaging-rabbit"
"parts/section-service-user"
"ca-bundle.pem.j2"
)
CONFIG_TEMPLATES_CINDER_CEPH=(
"parts/section-database"
"parts/database-connection"
"parts/database-connection-settings"
"parts/section-oslo-messaging-rabbit"
"parts/section-oslo-notifications"
)
CONFIG_TEMPLATES_DESIGNATE=(
"parts/database-connection"
"parts/database-connection-settings"
"parts/section-identity"
"parts/identity-data"
"parts/section-oslo-messaging-rabbit"
"parts/section-service-user"
"ca-bundle.pem.j2"
)
CONFIG_TEMPLATES_GLANCE=(
"parts/section-database"
"parts/database-connection"
"parts/database-connection-settings"
"parts/section-identity"
"parts/identity-data"
"parts/section-oslo-messaging-rabbit"
"parts/section-oslo-notifications"
"parts/section-service-user"
"ca-bundle.pem.j2"
)
CONFIG_TEMPLATES_GNOCCHI=(
"parts/database-connection"
"parts/section-identity"
"parts/identity-data"
"ca-bundle.pem.j2"
)
CONFIG_TEMPLATES_HEAT=(
"parts/section-database"
"parts/database-connection"
"parts/database-connection-settings"
"parts/section-identity"
"parts/identity-data"
"parts/section-trustee"
"parts/section-oslo-messaging-rabbit"
"ca-bundle.pem.j2"
)
CONFIG_TEMPLATES_HORIZON=(
"ca-bundle.pem.j2"
)
CONFIG_TEMPLATES_KEYSTONE=(
"parts/section-database"
"parts/database-connection"
"parts/database-connection-settings"
"parts/section-federation"
"parts/section-middleware"
"parts/section-oslo-cache"
"parts/section-oslo-messaging-rabbit"
"parts/section-oslo-middleware"
"parts/section-oslo-notifications"
"parts/section-signing"
)
CONFIG_TEMPLATES_MAGNUM=(
"parts/section-database"
"parts/database-connection"
"parts/database-connection-settings"
"parts/section-identity"
"parts/identity-data"
"parts/section-oslo-messaging-rabbit"
"parts/section-service-user"
"parts/section-trust"
"ca-bundle.pem.j2"
)
CONFIG_TEMPLATES_NEUTRON=(
"parts/section-database"
"parts/database-connection"
"parts/database-connection-settings"
"parts/section-identity"
"parts/identity-data"
"parts/section-oslo-messaging-rabbit"
"parts/section-service-user"
"ca-bundle.pem.j2"
)
CONFIG_TEMPLATES_NOVA=${CONFIG_TEMPLATES_NEUTRON[@]}
CONFIG_TEMPLATES_OCTAVIA=(
"parts/section-database"
"parts/database-connection"
"parts/database-connection-settings"
"parts/section-identity"
"parts/identity-data"
"ca-bundle.pem.j2"
)
CONFIG_TEMPLATES_PLACEMENT=(
"parts/database-connection"
"parts/database-connection-settings"
"parts/section-identity"
"parts/identity-data"
"parts/section-service-user"
"ca-bundle.pem.j2"
)
declare -A INTERNAL_LIBS=(
[aodh-k8s]=${INTERNAL_KEYSTONE_LIBS[@]}
[barbican-k8s]=${INTERNAL_KEYSTONE_LIBS[@]}
[ceilometer-k8s]=${INTERNAL_CEILOMETER_LIBS[@]}
[cinder-k8s]=${INTERNAL_CINDER_LIBS[@]}
[cinder-ceph-k8s]=${INTERNAL_CINDER_CEPH_LIBS[@]}
[designate-k8s]=${INTERNAL_DESIGNATE_LIBS[@]}
[designate-bind-k8s]=${INTERNAL_DESIGNATE_BIND_LIBS[@]}
[glance-k8s]=${INTERNAL_KEYSTONE_LIBS[@]}
[gnocchi-k8s]=${INTERNAL_GNOCCHI_LIBS[@]}
[heat-k8s]=${INTERNAL_KEYSTONE_LIBS[@]}
[horizon-k8s]=${INTERNAL_KEYSTONE_LIBS[@]}
[keystone-k8s]=${INTERNAL_KEYSTONE_LIBS[@]}
[keystone-ldap-k8s]=${INTERNAL_KEYSTONE_LIBS[@]}
[magnum-k8s]=${INTERNAL_KEYSTONE_LIBS[@]}
[neutron-k8s]=${INTERNAL_NEUTRON_LIBS[@]}
[nova-k8s]=${INTERNAL_NOVA_LIBS[@]}
[octavia-k8s]=${INTERNAL_NEUTRON_LIBS[@]}
[openstack-exporter-k8s]=${INTERNAL_KEYSTONE_LIBS[@]}
[openstack-hypervisor]=${INTERNAL_OPENSTACK_HYPERVISOR_LIBS[@]}
[openstack-images-sync-k8s]=${INTERNAL_OPENSTACK_IMAGES_SYNC_LIBS[@]}
[sunbeam-clusterd]=${NULL_ARRAY[@]}
[sunbeam-machine]=${NULL_ARRAY[@]}
[ovn-central-k8s]=${INTERNAL_OVN_CENTRAL_LIBS[@]}
[ovn-relay-k8s]=${INTERNAL_OVN_CENTRAL_LIBS[@]}
[placement-k8s]=${INTERNAL_KEYSTONE_LIBS[@]}
[tempest-k8s]=${INTERNAL_KEYSTONE_LIBS[@]}
)
declare -A EXTERNAL_LIBS=(
[aodh-k8s]=${EXTERNAL_AODH_LIBS[@]}
[barbican-k8s]=${EXTERNAL_BARBICAN_LIBS[@]}
[ceilometer-k8s]=${EXTERNAL_CEILOMETER_LIBS[@]}
[cinder-k8s]=${EXTERNAL_AODH_LIBS[@]}
[cinder-ceph-k8s]=${EXTERNAL_CINDER_CEPH_LIBS[@]}
[designate-k8s]=${EXTERNAL_AODH_LIBS[@]}
[designate-bind-k8s]=${EXTERNAL_DESIGNATE_BIND_LIBS[@]}
[glance-k8s]=${EXTERNAL_AODH_LIBS[@]}
[gnocchi-k8s]=${EXTERNAL_AODH_LIBS[@]}
[heat-k8s]=${EXTERNAL_HEAT_LIBS[@]}
[horizon-k8s]=${EXTERNAL_AODH_LIBS[@]}
[keystone-k8s]=${EXTERNAL_AODH_LIBS[@]}
[keystone-ldap-k8s]=${NULL_ARRAY[@]}
[magnum-k8s]=${EXTERNAL_AODH_LIBS[@]}
[neutron-k8s]=${EXTERNAL_NEUTRON_LIBS[@]}
[nova-k8s]=${EXTERNAL_NOVA_LIBS[@]}
[octavia-k8s]=${EXTERNAL_OCTAVIA_LIBS[@]}
[openstack-exporter-k8s]=${EXTERNAL_OPENSTACK_EXPORTER_LIBS[@]}
[openstack-hypervisor]=${EXTERNAL_OPENSTACK_HYPERVISOR_LIBS[@]}
[openstack-images-sync-k8s]=${EXTERNAL_OPENSTACK_IMAGES_SYNC_LIBS[@]}
[sunbeam-clusterd]=${EXTERNAL_SUNBEAM_CLUSTERD_LIBS[@]}
[sunbeam-machine]=${EXTERNAL_SUNBEAM_MACHINE_LIBS[@]}
[ovn-central-k8s]=${EXTERNAL_OVN_CENTRAL_LIBS[@]}
[ovn-relay-k8s]=${EXTERNAL_OVN_RELAY_LIBS[@]}
[placement-k8s]=${EXTERNAL_AODH_LIBS[@]}
[tempest-k8s]=${EXTERNAL_TEMPEST_LIBS[@]}
)
declare -A CONFIG_TEMPLATES=(
[aodh-k8s]=${CONFIG_TEMPLATES_AODH[@]}
[barbican-k8s]=${CONFIG_TEMPLATES_BARBICAN[@]}
[ceilometer-k8s]=${CONFIG_TEMPLATES_CEILOMETER[@]}
[cinder-k8s]=${CONFIG_TEMPLATES_CINDER[@]}
[cinder-ceph-k8s]=${CONFIG_TEMPLATES_CINDER_CEPH[@]}
[designate-k8s]=${CONFIG_TEMPLATES_DESIGNATE[@]}
[designate-bind-k8s]=${NULL_ARRAY[@]}
[glance-k8s]=${CONFIG_TEMPLATES_GLANCE[@]}
[gnocchi-k8s]=${CONFIG_TEMPLATES_GNOCCHI[@]}
[heat-k8s]=${CONFIG_TEMPLATES_HEAT[@]}
[horizon-k8s]=${CONFIG_TEMPLATES_HORIZON[@]}
[keystone-k8s]=${CONFIG_TEMPLATES_KEYSTONE[@]}
[keystone-ldap-k8s]=${NULL_ARRAY[@]}
[magnum-k8s]=${CONFIG_TEMPLATES_MAGNUM[@]}
[neutron-k8s]=${CONFIG_TEMPLATES_NEUTRON[@]}
[nova-k8s]=${CONFIG_TEMPLATES_NOVA[@]}
[octavia-k8s]=${CONFIG_TEMPLATES_OCTAVIA[@]}
[openstack-exporter-k8s]=${CONFIG_TEMPLATES_HORIZON[@]}
[openstack-hypervisor]=${NULL_ARRAY[@]}
[openstack-images-sync-k8s]=${NULL_ARRAY[@]}
[sunbeam-clusterd]=${NULL_ARRAY[@]}
[sunbeam-machine]=${NULL_ARRAY[@]}
[ovn-central-k8s]=${NULL_ARRAY[@]}
[ovn-relay-k8s]=${NULL_ARRAY[@]}
[placement-k8s]=${CONFIG_TEMPLATES_PLACEMENT[@]}
[tempest-k8s]=${NULL_ARRAY[@]}
)
function copy_ops_sunbeam {
cp -rf ../../ops-sunbeam/ops_sunbeam lib/
}
function copy_internal_libs {
internal_libs_=${INTERNAL_LIBS[$1]}
echo "copy_internal_libs for $1:"
for lib in ${internal_libs_[@]}; do
echo "Copying $lib"
cp -rf ../../libs/internal/lib/charms/$lib lib/charms/
done
}
function copy_external_libs {
echo "copy_external_libs for $1:"
external_libs_=${EXTERNAL_LIBS[$1]}
for lib in ${external_libs_[@]}; do
echo "Copying $lib"
cp -rf ../../libs/external/lib/charms/$lib lib/charms/
done
}
function copy_config_templates {
echo "copy_config_templates for $1:"
config_templates_=${CONFIG_TEMPLATES[$1]}
for part in ${config_templates_[@]}; do
echo "Copying $part"
cp -rf ../../templates/$part src/templates/$part
done
}
function copy_juju_ignore {
cp ../../.jujuignore .
}
function copy_stestr_conf {
cp ../../.stestr.conf .
}
function remove_libs {
rm -rf lib
}
function remove_config_templates {
echo "remove_config_templates for $1:"
config_templates_=${CONFIG_TEMPLATES[$1]}
for part in ${config_templates_[@]}; do
echo "Removing $part"
rm src/templates/$part
done
if (test -d src/templates/parts) && (test -n "$(find src/templates/parts -maxdepth 0 -empty)")
then
remove_templates_parts_dir
fi
}
function remove_templates_parts_dir {
rm -rf src/templates/parts
}
function remove_juju_ignore {
rm .jujuignore
}
function remove_stestr_conf {
rm .stestr.conf
}
function push_common_files {
if [[ $# != 1 ]];
then
echo "push_common_files: Expected one argument"
exit 1
fi
pushd charms/$1
mkdir -p lib/charms
mkdir -p src/templates/parts
copy_ops_sunbeam
copy_internal_libs $1
copy_external_libs $1
copy_config_templates $1
copy_stestr_conf
copy_juju_ignore
popd
}
function pop_common_files {
pushd charms/$1
remove_libs
remove_config_templates $1
remove_stestr_conf
remove_juju_ignore
popd
}
function copy_libs_for_ops_sunbeam {
mkdir -p tests/lib
cp -rf ../libs/external/lib ../libs/internal/lib tests/
}
function remove_libs_for_ops_sunbeam {
rm -rf tests/lib
}

View File

@ -1,22 +0,0 @@
#!/bin/bash
pushd libs/external
echo "INFO: Fetching libs from charmhub."
charmcraft fetch-lib charms.certificate_transfer_interface.v0.certificate_transfer
charmcraft fetch-lib charms.data_platform_libs.v0.data_interfaces
charmcraft fetch-lib charms.grafana_k8s.v0.grafana_dashboard
charmcraft fetch-lib charms.grafana_agent.v0.cos_agent
charmcraft fetch-lib charms.loki_k8s.v1.loki_push_api
charmcraft fetch-lib charms.observability_libs.v0.juju_topology
charmcraft fetch-lib charms.observability_libs.v1.kubernetes_service_patch
charmcraft fetch-lib charms.operator_libs_linux.v0.sysctl
charmcraft fetch-lib charms.operator_libs_linux.v2.snap
charmcraft fetch-lib charms.prometheus_k8s.v0.prometheus_scrape
charmcraft fetch-lib charms.rabbitmq_k8s.v0.rabbitmq
charmcraft fetch-lib charms.tls_certificates_interface.v3.tls_certificates
charmcraft fetch-lib charms.traefik_k8s.v2.ingress
charmcraft fetch-lib charms.traefik_route_k8s.v0.traefik_route
charmcraft fetch-lib charms.vault_k8s.v0.vault_kv
popd

View File

@ -1,301 +0,0 @@
# Copyright 2022 Canonical Ltd.
# See LICENSE file for licensing details.
"""## Overview.
This document explains how to use the `JujuTopology` class to
create and consume topology information from Juju in a consistent manner.
The goal of the Juju topology is to uniquely identify a piece
of software running across any of your Juju-managed deployments.
This is achieved by combining the following four elements:
- Model name
- Model UUID
- Application name
- Unit identifier
For a more in-depth description of the concept, as well as a
walk-through of it's use-case in observability, see
[this blog post](https://juju.is/blog/model-driven-observability-part-2-juju-topology-metrics)
on the Juju blog.
## Library Usage
This library may be used to create and consume `JujuTopology` objects.
The `JujuTopology` class provides three ways to create instances:
### Using the `from_charm` method
Enables instantiation by supplying the charm as an argument. When
creating topology objects for the current charm, this is the recommended
approach.
```python
topology = JujuTopology.from_charm(self)
```
### Using the `from_dict` method
Allows for instantion using a dictionary of relation data, like the
`scrape_metadata` from Prometheus or the labels of an alert rule. When
creating topology objects for remote charms, this is the recommended
approach.
```python
scrape_metadata = json.loads(relation.data[relation.app].get("scrape_metadata", "{}"))
topology = JujuTopology.from_dict(scrape_metadata)
```
### Using the class constructor
Enables instantiation using whatever values you want. While this
is useful in some very specific cases, this is almost certainly not
what you are looking for as setting these values manually may
result in observability metrics which do not uniquely identify a
charm in order to provide accurate usage reporting, alerting,
horizontal scaling, or other use cases.
```python
topology = JujuTopology(
model="some-juju-model",
model_uuid="00000000-0000-0000-0000-000000000001",
application="fancy-juju-application",
unit="fancy-juju-application/0",
charm_name="fancy-juju-application-k8s",
)
```
"""
from collections import OrderedDict
from typing import Dict, List, Optional
from uuid import UUID
# The unique Charmhub library identifier, never change it
LIBID = "bced1658f20f49d28b88f61f83c2d232"
LIBAPI = 0
LIBPATCH = 6
class InvalidUUIDError(Exception):
"""Invalid UUID was provided."""
def __init__(self, uuid: str):
self.message = "'{}' is not a valid UUID.".format(uuid)
super().__init__(self.message)
class JujuTopology:
"""JujuTopology is used for storing, generating and formatting juju topology information.
DEPRECATED: This class is deprecated. Use `pip install cosl` and
`from cosl.juju_topology import JujuTopology` instead.
"""
def __init__(
self,
model: str,
model_uuid: str,
application: str,
unit: Optional[str] = None,
charm_name: Optional[str] = None,
):
"""Build a JujuTopology object.
A `JujuTopology` object is used for storing and transforming
Juju topology information. This information is used to
annotate Prometheus scrape jobs and alert rules. Such
annotation when applied to scrape jobs helps in identifying
the source of the scrapped metrics. On the other hand when
applied to alert rules topology information ensures that
evaluation of alert expressions is restricted to the source
(charm) from which the alert rules were obtained.
Args:
model: a string name of the Juju model
model_uuid: a globally unique string identifier for the Juju model
application: an application name as a string
unit: a unit name as a string
charm_name: name of charm as a string
"""
if not self.is_valid_uuid(model_uuid):
raise InvalidUUIDError(model_uuid)
self._model = model
self._model_uuid = model_uuid
self._application = application
self._charm_name = charm_name
self._unit = unit
def is_valid_uuid(self, uuid):
"""Validate the supplied UUID against the Juju Model UUID pattern.
Args:
uuid: string that needs to be checked if it is valid v4 UUID.
Returns:
True if parameter is a valid v4 UUID, False otherwise.
"""
try:
return str(UUID(uuid, version=4)) == uuid
except (ValueError, TypeError):
return False
@classmethod
def from_charm(cls, charm):
"""Creates a JujuTopology instance by using the model data available on a charm object.
Args:
charm: a `CharmBase` object for which the `JujuTopology` will be constructed
Returns:
a `JujuTopology` object.
"""
return cls(
model=charm.model.name,
model_uuid=charm.model.uuid,
application=charm.model.app.name,
unit=charm.model.unit.name,
charm_name=charm.meta.name,
)
@classmethod
def from_dict(cls, data: dict):
"""Factory method for creating `JujuTopology` children from a dictionary.
Args:
data: a dictionary with five keys providing topology information. The keys are
- "model"
- "model_uuid"
- "application"
- "unit"
- "charm_name"
`unit` and `charm_name` may be empty, but will result in more limited
labels. However, this allows us to support charms without workloads.
Returns:
a `JujuTopology` object.
"""
return cls(
model=data["model"],
model_uuid=data["model_uuid"],
application=data["application"],
unit=data.get("unit", ""),
charm_name=data.get("charm_name", ""),
)
def as_dict(
self,
*,
remapped_keys: Optional[Dict[str, str]] = None,
excluded_keys: Optional[List[str]] = None,
) -> OrderedDict:
"""Format the topology information into an ordered dict.
Keeping the dictionary ordered is important to be able to
compare dicts without having to resort to deep comparisons.
Args:
remapped_keys: A dictionary mapping old key names to new key names,
which will be substituted when invoked.
excluded_keys: A list of key names to exclude from the returned dict.
uuid_length: The length to crop the UUID to.
"""
ret = OrderedDict(
[
("model", self.model),
("model_uuid", self.model_uuid),
("application", self.application),
("unit", self.unit),
("charm_name", self.charm_name),
]
)
if excluded_keys:
ret = OrderedDict({k: v for k, v in ret.items() if k not in excluded_keys})
if remapped_keys:
ret = OrderedDict(
(remapped_keys.get(k), v) if remapped_keys.get(k) else (k, v) for k, v in ret.items() # type: ignore
)
return ret
@property
def identifier(self) -> str:
"""Format the topology information into a terse string.
This crops the model UUID, making it unsuitable for comparisons against
anything but other identifiers. Mainly to be used as a display name or file
name where long strings might become an issue.
>>> JujuTopology( \
model = "a-model", \
model_uuid = "00000000-0000-4000-8000-000000000000", \
application = "some-app", \
unit = "some-app/1" \
).identifier
'a-model_00000000_some-app'
"""
parts = self.as_dict(
excluded_keys=["unit", "charm_name"],
)
parts["model_uuid"] = self.model_uuid_short
values = parts.values()
return "_".join([str(val) for val in values]).replace("/", "_")
@property
def label_matcher_dict(self) -> Dict[str, str]:
"""Format the topology information into a dict with keys having 'juju_' as prefix.
Relabelled topology never includes the unit as it would then only match
the leader unit (ie. the unit that produced the dict).
"""
items = self.as_dict(
remapped_keys={"charm_name": "charm"},
excluded_keys=["unit"],
).items()
return {"juju_{}".format(key): value for key, value in items if value}
@property
def label_matchers(self) -> str:
"""Format the topology information into a promql/logql label matcher string.
Topology label matchers should never include the unit as it
would then only match the leader unit (ie. the unit that
produced the matchers).
"""
items = self.label_matcher_dict.items()
return ", ".join(['{}="{}"'.format(key, value) for key, value in items if value])
@property
def model(self) -> str:
"""Getter for the juju model value."""
return self._model
@property
def model_uuid(self) -> str:
"""Getter for the juju model uuid value."""
return self._model_uuid
@property
def model_uuid_short(self) -> str:
"""Getter for the juju model value, truncated to the first eight letters."""
return self._model_uuid[:8]
@property
def application(self) -> str:
"""Getter for the juju application value."""
return self._application
@property
def charm_name(self) -> Optional[str]:
"""Getter for the juju charm name value."""
return self._charm_name
@property
def unit(self) -> Optional[str]:
"""Getter for the juju unit value."""
return self._unit

View File

@ -3,7 +3,7 @@ jsonschema
kubernetes
ops
python-keystoneclient
git+https://github.com/openstack/charm-ops-interface-ceph-client#egg=interface_ceph_client
git+https://opendev.org/openstack/charm-ops-interface-ceph-client#egg=interface_ceph_client
lightkube
lightkube-models
tenacity

388
repository.py Executable file
View File

@ -0,0 +1,388 @@
#!/usr/bin/env python
import logging
import argparse
import dataclasses
import pathlib
import shutil
import subprocess
import yaml
ROOT_DIR = pathlib.Path(__file__).parent
EXTERNAL_LIB_DIR = ROOT_DIR / "libs" / "external" / "lib"
OPS_SUNBEAM_DIR = ROOT_DIR / "ops-sunbeam" / "ops_sunbeam"
BUILD_FILE = ".sunbeam-build.yaml"
UTILITY_FILES = [
ROOT_DIR / ".stestr.conf",
ROOT_DIR / ".jujuignore",
]
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)
###############################################
# Utility functions
###############################################
@dataclasses.dataclass
class SunbeamBuild:
path: pathlib.Path
external_libraries: list[str]
internal_libraries: list[str]
templates: list[str]
@classmethod
def load(cls, path: pathlib.Path) -> "SunbeamBuild":
with path.open() as f:
data = yaml.safe_load(f)
return cls(
path=path.parent,
external_libraries=data.get("external-libraries", []),
internal_libraries=data.get("internal-libraries", []),
templates=data.get("templates", []),
)
def _library_to_path(library: str) -> pathlib.Path:
split = library.split(".")
if len(split) != 4:
raise ValueError(f"Invalid library: {library}")
return pathlib.Path("/".join(split) + ".py")
def validate_charm(
charm: str,
internal_libraries: dict[str, pathlib.Path],
external_libraries: dict[str, pathlib.Path],
templates: dict[str, pathlib.Path],
) -> SunbeamBuild:
"""Validate the charm."""
path = ROOT_DIR / "charms" / charm
if not path.exists():
raise ValueError(f"Charm {charm} does not exist.")
build_file = path / BUILD_FILE
if not build_file.exists():
raise ValueError(f"Charm {charm} does not have a build file.")
charm_build = load_charm(charm)
for library in charm_build.external_libraries:
if library not in external_libraries:
raise ValueError(
f"Charm {charm} has invalid external library: {library} not found."
)
for library in charm_build.internal_libraries:
if library not in internal_libraries:
raise ValueError(
f"Charm {charm} has invalid internal library: {library} not found."
)
for template in charm_build.templates:
if template not in templates:
raise ValueError(
f"Charm {charm} has invalid template: {template} not found."
)
return charm_build
def load_external_libraries() -> dict[str, pathlib.Path]:
"""Load the external libraries."""
path = EXTERNAL_LIB_DIR
return {
str(p.relative_to(path))[:-3].replace("/", "."): p
for p in path.glob("**/*.py")
}
def load_internal_libraries() -> dict[str, pathlib.Path]:
"""Load the internal libraries."""
charms = list((ROOT_DIR / "charms").iterdir())
libraries = {}
for charm in charms:
path = charm / "lib"
search_path = path / "charms" / charm.name.replace("-", "_")
libraries.update(
{
str(p.relative_to(path))[:-3].replace("/", "."): p
for p in search_path.glob("**/*.py")
}
)
return libraries
def load_templates() -> dict[str, pathlib.Path]:
"""Load the templates."""
path = ROOT_DIR / "templates"
return {str(p.relative_to(path)): p for p in path.glob("**/*")}
def list_charms() -> list[str]:
"""List the available charms."""
return [p.name for p in (ROOT_DIR / "charms").iterdir() if p.is_dir()]
def load_charm(charm: str) -> SunbeamBuild:
"""Load the charm build file."""
path = ROOT_DIR / "charms" / charm / BUILD_FILE
return SunbeamBuild.load(path)
def copy(src: pathlib.Path, dest: pathlib.Path):
"""Copy the src to dest.
Only supports files.
"""
dest.parent.mkdir(parents=True, exist_ok=True)
shutil.copy(src, dest)
def prepare_charm(
charm: SunbeamBuild,
internal_libraries: dict[str, pathlib.Path],
external_libraries: dict[str, pathlib.Path],
templates: dict[str, pathlib.Path],
dry_run: bool = False,
):
"""Copy the necessary files.
Will copy external libraries, ops sunbeam and templates.
"""
dest = charm.path / "lib" / "ops_sunbeam"
logger.debug(f"Copying ops sunbeam to {dest}")
if not dry_run:
dest.parent.mkdir(parents=True, exist_ok=True)
shutil.copytree(OPS_SUNBEAM_DIR, dest)
for utility_file in UTILITY_FILES:
utility_path = utility_file.relative_to(ROOT_DIR)
dest = charm.path / utility_path
logger.debug(f"Copying {utility_file} to {dest}")
if not dry_run:
copy(utility_file, dest)
for library in charm.external_libraries:
path = external_libraries[library]
library_path = path.relative_to(EXTERNAL_LIB_DIR)
dest = charm.path / "lib" / library_path
if not dest.exists():
logger.debug(f"Copying {library} to {dest}")
if dry_run:
continue
copy(path, dest)
for library in charm.internal_libraries:
path = internal_libraries[library]
library_path = _library_to_path(library)
dest = charm.path / "lib" / library_path
if not dest.exists():
logger.debug(f"Copying {library} to {dest}")
if dry_run:
continue
copy(path, dest)
for template in charm.templates:
path = templates[template]
dest = charm.path / "src" / "templates" / template
if not dest.exists():
logger.debug(f"Copying {template} to {dest}")
if dry_run:
continue
copy(path, dest)
def clean_charm(
charm: SunbeamBuild,
dry_run: bool = False,
):
"""Clean charm directory.
Will remove the external libraries, ops sunbeam and templates.
"""
path = charm.path / "lib" / "ops_sunbeam"
if path.exists():
logger.debug(f"Removing {path}")
if not dry_run:
shutil.rmtree(path)
for utility_file in UTILITY_FILES:
utility_path = utility_file.relative_to(ROOT_DIR)
path = charm.path / utility_path
if path.exists():
logger.debug(f"Removing {path}")
if not dry_run:
path.unlink()
for library in charm.external_libraries + charm.internal_libraries:
# Remove the charm namespace
path = charm.path / "lib" / _library_to_path(library).parents[1]
if path.exists():
logger.debug(f"Removing {path}")
if dry_run:
continue
shutil.rmtree(path)
for template in charm.templates:
path = charm.path / "src" / "templates" / template
if path.exists():
logger.debug(f"Removing {path}")
if dry_run:
continue
path.unlink()
###############################################
# Cli Definitions
###############################################
def _add_charm_argument(parser: argparse.ArgumentParser):
parser.add_argument(
"charm", type=str, nargs="*", help="The charm to operate on."
)
def main_cli():
main_parser = argparse.ArgumentParser(
description="Sunbeam Repository utilities."
)
main_parser.add_argument(
"-v", "--verbose", action="store_true", help="Enable verbose logging."
)
subparsers = main_parser.add_subparsers(
required=True, help="sub-command help"
)
prepare_parser = subparsers.add_parser("prepare", help="Prepare charm(s).")
_add_charm_argument(prepare_parser)
prepare_parser.add_argument(
"--clean",
action="store_true",
default=False,
help="Clean the charm(s) first.",
)
prepare_parser.add_argument(
"--dry-run", action="store_true", default=False, help="Dry run."
)
prepare_parser.set_defaults(func=prepare_cli)
clean_parser = subparsers.add_parser("clean", help="Clean charm(s).")
_add_charm_argument(clean_parser)
clean_parser.add_argument(
"--dry-run", action="store_true", default=False, help="Dry run."
)
clean_parser.set_defaults(func=clean_cli)
validate_parser = subparsers.add_parser(
"validate", help="Validate charm(s)."
)
_add_charm_argument(validate_parser)
validate_parser.set_defaults(func=validate_cli)
pythonpath_parser = subparsers.add_parser(
"pythonpath", help="Print the pythonpath."
)
pythonpath_parser.set_defaults(func=pythonpath_cli)
fetch_lib_parser = subparsers.add_parser(
"fetch-lib", help="Fetch the external libraries."
)
fetch_lib_parser.add_argument(
"libraries", type=str, nargs="*", help="Libraries to fetch."
)
fetch_lib_parser.set_defaults(func=fetch_lib_cli)
args = main_parser.parse_args()
level = logging.INFO
if args.verbose:
level = logging.DEBUG
logger.setLevel(level)
context = vars(args)
context["internal_libraries"] = load_internal_libraries()
context["external_libraries"] = load_external_libraries()
context["templates"] = load_templates()
context["sunbeam_charms"] = list_charms()
if "charm" in context:
charms = context.pop("charm")
if not charms:
charms = context["sunbeam_charms"]
context["charms"] = [
validate_charm(
charm,
context["internal_libraries"],
context["external_libraries"],
context["templates"],
)
for charm in charms
]
args.func(**context)
def prepare_cli(
charms: list[SunbeamBuild],
internal_libraries: dict[str, pathlib.Path],
external_libraries: dict[str, pathlib.Path],
templates: dict[str, pathlib.Path],
clean: bool = False,
dry_run: bool = False,
**kwargs,
):
for charm in charms:
logger.info("Preparing the charm %s", charm.path.name)
if clean:
clean_charm(charm, dry_run=dry_run)
prepare_charm(
charm,
internal_libraries,
external_libraries,
templates,
dry_run=dry_run,
)
def clean_cli(
charms: list[SunbeamBuild],
internal_libraries: dict[str, pathlib.Path],
external_libraries: dict[str, pathlib.Path],
templates: dict[str, pathlib.Path],
dry_run: bool = False,
**kwargs,
):
for charm in charms:
logger.info("Cleaning the charm %s", charm.path.name)
clean_charm(charm, dry_run=dry_run)
def validate_cli(
charms: list[SunbeamBuild],
internal_libraries: dict[str, pathlib.Path],
external_libraries: dict[str, pathlib.Path],
templates: dict[str, pathlib.Path],
**kwargs,
):
"""No op because done in the main_cli."""
for charm in charms:
logging.info("Charm %s is valid.", charm.path.name)
def pythonpath_cli(internal_libraries: dict[str, pathlib.Path], **kwargs):
"""Print the pythonpath."""
parent_dirs = set()
for path in internal_libraries.values():
parent_dirs.add(path.parents[3])
parent_dirs.add(OPS_SUNBEAM_DIR.parent)
parent_dirs.add(EXTERNAL_LIB_DIR)
print(":".join(str(p) for p in parent_dirs))
def fetch_lib_cli(
libraries: list[str], external_libraries: dict[str, pathlib.Path], **kwargs
):
"""Fetch the external libraries."""
cwd = EXTERNAL_LIB_DIR.parent
libraries_set = set(libraries)
unknown_libraries = libraries_set - set(external_libraries.keys())
if unknown_libraries:
raise ValueError(f"Unknown libraries: {unknown_libraries}")
if not libraries_set:
libraries_set = set(external_libraries.keys())
for library in libraries_set:
logging.info(f"Fetching {library}")
# Fetch the library
subprocess.run(
["charmcraft", "fetch-lib", library], cwd=cwd, check=True
)
if __name__ == "__main__":
main_cli()

View File

@ -2,14 +2,12 @@
set -o xtrace
source common.sh
# print checks to test based on the first arg
get_charms_to_test() {
local charm=$1
if [[ -z "$charm" ]]; then
ls charms
elif [[ "$charm" = "ops-sunbeam" ]]; then
elif [[ "$charm" = "ops-sunbeam" ]]; then
# ops-sunbeam is treated differently, so don't process it here
false
else
@ -69,40 +67,38 @@ then
# Run py3 on ops-sunbeam
if should_test_ops_sunbeam $2; then
path_python=$(python3 ./repository.py pythonpath)
pushd ops-sunbeam
copy_libs_for_ops_sunbeam
stestr run --slowest || exit 1
remove_libs_for_ops_sunbeam
PYTHONPATH=$path_python stestr run --slowest || exit 1
popd
fi
# Run py3 on sunbeam charms
for charm in $(get_charms_to_test $2); do
push_common_files $charm || exit 1
python3 repository.py -v prepare --clean $charm || exit 1
pushd charms/$charm
PYTHONPATH=./src:./lib stestr run --slowest || exit 1
popd
pop_common_files $charm || exit 1
python3 repository.py -v clean $charm || exit 1
done
elif [[ $1 == "cover" ]];
then
coverage erase
coverage erase
# Run coverage on ops-sunbeam
if should_test_ops_sunbeam $2; then
path_python=$(python3 ./repository.py pythonpath)
pushd ops-sunbeam
copy_libs_for_ops_sunbeam
coverage erase
PYTHON="coverage run --parallel-mode --omit .tox/*" stestr run --slowest || exit 1
PYTHONPATH=$path_python PYTHON="coverage run --parallel-mode --omit .tox/*" stestr run --slowest || exit 1
coverage combine
remove_libs_for_ops_sunbeam
popd
fi
# Run coverage on sunbeam charms
for charm in $(get_charms_to_test $2); do
push_common_files $charm || exit 1
python3 repository.py -v prepare --clean $charm || exit 1
pushd charms/$charm
coverage erase
PYTHONPATH=./src:./lib:../../ops-sunbeam PYTHON="coverage run --parallel-mode --omit .tox/*,src/templates/*" stestr run --slowest || exit 1
@ -123,7 +119,7 @@ then
# Common files should be deleted after coverage combine
for charm in $(get_charms_to_test $2); do
pop_common_files $charm || exit 1
python3 repository.py -v clean $charm || exit 1
done
elif [[ $1 == "build" ]];
@ -142,7 +138,7 @@ then
exit 1
fi
push_common_files $charm || exit 1
python3 repository.py -v prepare --clean $charm || exit 1
pushd charms/$charm || exit 1
charmcraft -v pack || exit 1
if [[ -e "${charm}.charm" ]];
@ -155,8 +151,9 @@ then
mv ${charm}_*.charm ${charm}.charm
popd || exit 1
pop_common_files $charm || exit 1
cp charms/$charm/${charm}.charm . || exit 1
python3 repository.py -v clean $charm || exit 1
else
echo "tox argument should be one of pep8, py3, py310, py311, cover";
exit 1
exit 1
fi

11
tox.ini
View File

@ -14,20 +14,21 @@ passenv =
HOME
allowlist_externals =
{toxinidir}/run_tox.sh
{toxinidir}/fetch_libs.sh
{toxinidir}/generate_token.sh
{toxinidir}/repository.py
[testenv:fetch]
basepython = python3
deps =
deps = pyyaml
commands =
{toxinidir}/fetch_libs.sh
python3 {toxinidir}/repository.py -v fetch-lib {posargs}
[testenv:fmt]
description = Apply coding style standards to code
deps =
black
isort
pyyaml
commands =
{toxinidir}/run_tox.sh fmt
@ -43,12 +44,14 @@ deps =
pep8-naming
isort
codespell
pyyaml
commands =
{toxinidir}/run_tox.sh pep8
[testenv:py3]
deps =
-r{toxinidir}/test-requirements.txt
pyyaml
commands =
{toxinidir}/run_tox.sh py3 {posargs}
@ -69,7 +72,7 @@ commands =
[testenv:build]
basepython = python3
deps =
deps = pyyaml
commands =
{toxinidir}/run_tox.sh build {posargs}