)]}'
{"/PATCHSET_LEVEL":[{"author":{"_account_id":39104,"name":"Gunnar Hovik","display_name":"guno327","email":"gunnar.hovik@canonical.com","username":"guno327"},"change_message_id":"031762a77bd4e08a0fd446c9685d675dcd88ee58","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":3,"id":"698aef75_d15eb640","updated":"2026-05-05 12:08:40.000000000","message":"Thank you for your feedback, I have responded with rational for all decisions questioned and look forward to your advise on how to proceed.","commit_id":"09a2306c03eff5bd5727cb4b9a951cc0c3149733"},{"author":{"_account_id":35761,"name":"Guillaume Boutry","display_name":"gboutry","email":"guillaume.boutry@canonical.com","username":"gboutry"},"change_message_id":"a8b0fa02cc1626ba9e9200de3e8d1d787476e296","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":3,"id":"2d78f93b_931b34af","updated":"2026-05-05 09:03:25.000000000","message":"Thanks for the contribution,\n\nWe need to managed this with a named resource, mainly for:\n- traceability\n- ensure that the pod keeps the file. (refreshing the charm for example, will drop the temporary archive)\n\nPlease take as an example:\nhttps://documentation.ubuntu.com/ops/latest/howto/manage-resources/","commit_id":"09a2306c03eff5bd5727cb4b9a951cc0c3149733"},{"author":{"_account_id":39104,"name":"Gunnar Hovik","display_name":"guno327","email":"gunnar.hovik@canonical.com","username":"guno327"},"change_message_id":"031762a77bd4e08a0fd446c9685d675dcd88ee58","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":3,"id":"68b588e2_29b4be26","in_reply_to":"2d78f93b_931b34af","updated":"2026-05-05 12:08:40.000000000","message":"My choice to not use a managed resource was deliberate. As brought up by you in the product feedback jira ticket, the juju terraform provider does not have support for local file path resources. It does support managed resources but only pulling from charmhub or oci-container registries. Since this resource would not be an oci image you would need to upload the theme you wanted to use to charmhub beforehand in order for it to integrate with the terraform provider. This eliminates the core purpose of this feature by gating what themes can be used in the terraform provider (and thus sunbeam) to what is uploaded by the charmhub maintainers for this charm. It is possible I misunderstand who has upload permission for managed resources to a charm on charmhub, but regardless, I do not think end-users would want to expose every and all theme they us to all of charmhub.\n\nI do agree that my solution is hacky when compared to the intended way to manage files and it would not be needed if the intent was for people to define the resource path at deploy time (where local file paths WOULD be supported). However, since the whole purpose of this feature is to integrate with sunbeam we cannot use managed resources due to the terraform provider restrictions.\n\nMaybe my understanding of the interaction between the juju terraform provider and managed resources is incorrect, in which case I would be open to advice as I am somewhat of a novice when it comes to terraform.\n\n------------------------\n\nAs for the comment on refresh reverting the change, I would need to test this as I am not sure of the interaction. Does `configure_unit` not get called on refresh? I am not sure of the answer but claude seems convinced that it is in the codepath of every reconciliation that could happen to a charm (config_changed, pebble_ready, **upgrade_charm**, etc.) That fact that it should be in the codepath of pebble_ready event means that the file should exist even if the pod in k8s is manually destroyed and automatically reconciled from the Deployment (if my understanding of k8s+juju interaction is correct). If my understanding is missing something feel free to correct and advise on next steps.","commit_id":"09a2306c03eff5bd5727cb4b9a951cc0c3149733"},{"author":{"_account_id":39104,"name":"Gunnar Hovik","display_name":"guno327","email":"gunnar.hovik@canonical.com","username":"guno327"},"change_message_id":"0be4806f0f173cee40d36564701c5681e1089cf5","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":3,"id":"4b7dcde9_21b57d23","in_reply_to":"37bf13a9_888f7c16","updated":"2026-05-12 20:33:31.000000000","message":"Done","commit_id":"09a2306c03eff5bd5727cb4b9a951cc0c3149733"},{"author":{"_account_id":35761,"name":"Guillaume Boutry","display_name":"gboutry","email":"guillaume.boutry@canonical.com","username":"gboutry"},"change_message_id":"28f941e935d2fc59f5bb0bc6fa4115baf5e34d9c","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":3,"id":"c1ceb4eb_0671a98c","in_reply_to":"3f016b9e_71e2dd3b","updated":"2026-05-05 12:29:33.000000000","message":"That would essentially force in sunbeam to:\nresource_id\u003d$(juju attach-resource..)\nthen terraform apply -var\u003ddashboard-theme-resource\u003d$resource_id\n\n(this is just high level description)","commit_id":"09a2306c03eff5bd5727cb4b9a951cc0c3149733"},{"author":{"_account_id":39104,"name":"Gunnar Hovik","display_name":"guno327","email":"gunnar.hovik@canonical.com","username":"guno327"},"change_message_id":"697295ffd566b7c88bf7d86b92bedf10c5e34ecf","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":3,"id":"b4da174f_51d1ce99","in_reply_to":"4b7dcde9_21b57d23","updated":"2026-05-18 16:57:28.000000000","message":"IMPORTANT\nI started working on an implementation of consuming this revision in snap-openstack and by implementing this as a resource the default case of the user providing no custom-theme resource will break things.\n\nJuju has no way to make a resource optional so a theme filepath MUST be included ALWAYS. My code checks for a 0-byte file and just returns in that case so we can just attach a zero-byte file when the user wants no custom theme. \n\nHowever, in terraform there is no way to specify a resource to use in the default plans so it will be important to publish the charm with a 0-byte file included as the default resource for this charm to prevent the charm from failing to refresh in existing deployments to a new revision with this feature included.\n\nThis would not be an issue when implemented using a config option, but since managed resource seems to be a hard requirement here there will need to be some work at publish time to ensure continuity.","commit_id":"09a2306c03eff5bd5727cb4b9a951cc0c3149733"},{"author":{"_account_id":35761,"name":"Guillaume Boutry","display_name":"gboutry","email":"guillaume.boutry@canonical.com","username":"gboutry"},"change_message_id":"70a5b43df3773d7bdd9d8c59a4af8185296fc5d1","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":3,"id":"3f016b9e_71e2dd3b","in_reply_to":"68b588e2_29b4be26","updated":"2026-05-05 12:27:45.000000000","message":"We should be able to actual attach a local file as resource at deployment time, and map that local upload resource ID in terraform.","commit_id":"09a2306c03eff5bd5727cb4b9a951cc0c3149733"},{"author":{"_account_id":35761,"name":"Guillaume Boutry","display_name":"gboutry","email":"guillaume.boutry@canonical.com","username":"gboutry"},"change_message_id":"91efc2d2240c788bd41c12d68ab6a634bbe12b8c","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":3,"id":"d0159b9e_9e293c84","in_reply_to":"b4da174f_51d1ce99","updated":"2026-05-19 08:24:48.000000000","message":"\u003e This would not be an issue when implemented using a config option\n\nI don\u0027t see how this is true in any way.\n\nThen how do you manage getting the file into the container using only config option? Not using config option would lead to other problems, that are harder to manage on day 2 vs ensuring a 0 file byte is released when a new track of the charm.\n\nWe prefer to keep this kind of complexity on us (on our release pipeline / automated), rather than on day 2 ops.","commit_id":"09a2306c03eff5bd5727cb4b9a951cc0c3149733"},{"author":{"_account_id":39104,"name":"Gunnar Hovik","display_name":"guno327","email":"gunnar.hovik@canonical.com","username":"guno327"},"change_message_id":"7034d21d42735d920455a40e36064de3272b162b","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":3,"id":"37bf13a9_888f7c16","in_reply_to":"c1ceb4eb_0671a98c","updated":"2026-05-05 13:55:39.000000000","message":"Your explanation seems cromulent enough for me and my little knowledge of terraform. I will re implement with managed resource when I get the time.","commit_id":"09a2306c03eff5bd5727cb4b9a951cc0c3149733"},{"author":{"_account_id":39104,"name":"Gunnar Hovik","display_name":"guno327","email":"gunnar.hovik@canonical.com","username":"guno327"},"change_message_id":"a5d9bb3838a67fae0e976a1d39d19f3281700dfd","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":3,"id":"2340cb39_11575b56","in_reply_to":"d0159b9e_9e293c84","updated":"2026-05-19 14:46:01.000000000","message":"Both revisions I have made (config + resource) are both equally functional. I fear we are going to continue to disagree on this point and continued discourse is not productive at this point since resource is your mandate.\n\nAs long as the 0-byte file is handled by the CI then I am happy. Comment was just to make you (sunbeam team) aware of this caveat.","commit_id":"09a2306c03eff5bd5727cb4b9a951cc0c3149733"},{"author":{"_account_id":35761,"name":"Guillaume Boutry","display_name":"gboutry","email":"guillaume.boutry@canonical.com","username":"gboutry"},"change_message_id":"91efc2d2240c788bd41c12d68ab6a634bbe12b8c","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":9,"id":"cdef599e_f2bef560","updated":"2026-05-19 08:24:48.000000000","message":"Last nits. I\u0027ll take this PR for a spin and see how it behaves with terraform before EOW.","commit_id":"12064d6957e2d1dd6474c8c16af31f0e3d1dd6a0"},{"author":{"_account_id":39104,"name":"Gunnar Hovik","display_name":"guno327","email":"gunnar.hovik@canonical.com","username":"guno327"},"change_message_id":"589a535f925344f90d23bc477c62ea7200fa36ac","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":10,"id":"e6aaf8cf_a6085b02","updated":"2026-05-19 18:35:44.000000000","message":"I removed a single piece of whitespace and the CI fails, I am assuming this is a CI infra problem.","commit_id":"0275a100acb6db009a74dc45e5c50168e94e0aaf"},{"author":{"_account_id":39104,"name":"Gunnar Hovik","display_name":"guno327","email":"gunnar.hovik@canonical.com","username":"guno327"},"change_message_id":"a5d9bb3838a67fae0e976a1d39d19f3281700dfd","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":10,"id":"a351b4a3_e662cfa8","updated":"2026-05-19 14:46:01.000000000","message":"Pushed (hopefully) final patchset to address remaining comments. Will continue working on accompanying snap-openstack patch and will look for your updates regarding terraform interoperability.","commit_id":"0275a100acb6db009a74dc45e5c50168e94e0aaf"},{"author":{"_account_id":39104,"name":"Gunnar Hovik","display_name":"guno327","email":"gunnar.hovik@canonical.com","username":"guno327"},"change_message_id":"ad294900e693729157ea327420d942ca7b6e063e","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":10,"id":"e9ff47b1_3bd9bcbc","in_reply_to":"e6aaf8cf_a6085b02","updated":"2026-05-22 21:08:42.000000000","message":"recheck","commit_id":"0275a100acb6db009a74dc45e5c50168e94e0aaf"},{"author":{"_account_id":39104,"name":"Gunnar Hovik","display_name":"guno327","email":"gunnar.hovik@canonical.com","username":"guno327"},"change_message_id":"7df7bdf400436fd7f8cabc0329b0c7e30e7d46a7","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":10,"id":"3bfd1c69_ddfa5f4e","in_reply_to":"e9ff47b1_3bd9bcbc","updated":"2026-05-26 14:35:25.000000000","message":"Done","commit_id":"0275a100acb6db009a74dc45e5c50168e94e0aaf"},{"author":{"_account_id":35761,"name":"Guillaume Boutry","display_name":"gboutry","email":"guillaume.boutry@canonical.com","username":"gboutry"},"change_message_id":"015a1b7cc7a65e313867d582b61786ce3ff771a9","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":12,"id":"7e93a481_6c4616cb","updated":"2026-06-05 07:16:58.000000000","message":"@gunnar.hovik@canonical.com the functest failures are because main is now on 2026.1-26.04 artifacts and there are some compatibility issues. I\u0027m working to resolve those.","commit_id":"90777945a81239ffaac4b66f9267cb7fa9ad32a9"},{"author":{"_account_id":8992,"name":"Billy Olsen","email":"billy.olsen@canonical.com","username":"billy-olsen"},"change_message_id":"24e73bd0c54e8039a0ade8ebfdc6e8ca981a12eb","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":12,"id":"8ca92d25_1e046d5f","updated":"2026-06-09 02:03:22.000000000","message":"A couple of things after talking with @gunnar.hovik@canonical.com about this. It\u0027s tempting to add a bunch of config options in order to be able to allow users maximum flexibility, but it can also lead to confusion and place some of the mental burden on the user or the code that is managing the configuration. This is a scenario where it should Just Work (TM).\n\nWe should take an opinion - when a user uploads a new theme as a resource, the theme should contain enough information to fill everything out for the user. It is necessary to peek inside the tarball in order to do this, and I know it was intentionally avoided to do reads of the tarball, but the code was already doing unnecessary I/O by expanding a tarball that was already deployed.\n\nWhen the user uploads a custom theme, I think that the intent is clear that this is the theme the user wants to use. I find it unlikely that the user would want other themes to be available to the end user - as it often includes branding that is relevant and interesting to the user/organization that deployed it. Therefore, we should make the charm behave this way. User uploads a custom theme, that is the theme that is presented to the user - the only theme available in the system. If a user uploads another 0-length file, then that is the equivalent of removing the custom theme and restoring the default behavior.","commit_id":"90777945a81239ffaac4b66f9267cb7fa9ad32a9"},{"author":{"_account_id":39104,"name":"Gunnar Hovik","display_name":"guno327","email":"gunnar.hovik@canonical.com","username":"guno327"},"change_message_id":"bc52dc1e73ed2c14db98aa52db14b390d14d7d3c","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":12,"id":"11e09e03_1d64217e","updated":"2026-06-04 19:21:47.000000000","message":"Func test failures have nothing to do with this change. They appear to be in nova.","commit_id":"90777945a81239ffaac4b66f9267cb7fa9ad32a9"},{"author":{"_account_id":39104,"name":"Gunnar Hovik","display_name":"guno327","email":"gunnar.hovik@canonical.com","username":"guno327"},"change_message_id":"bc40fe3c4dc7cfaead61d78e602ce1e041c96926","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":12,"id":"0455e56e_a845e9e6","in_reply_to":"11e09e03_1d64217e","updated":"2026-06-09 16:23:05.000000000","message":"Done","commit_id":"90777945a81239ffaac4b66f9267cb7fa9ad32a9"},{"author":{"_account_id":39104,"name":"Gunnar Hovik","display_name":"guno327","email":"gunnar.hovik@canonical.com","username":"guno327"},"change_message_id":"bc40fe3c4dc7cfaead61d78e602ce1e041c96926","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":12,"id":"481f1468_c465e0c6","in_reply_to":"8ca92d25_1e046d5f","updated":"2026-06-09 16:23:05.000000000","message":"Done","commit_id":"90777945a81239ffaac4b66f9267cb7fa9ad32a9"},{"author":{"_account_id":39104,"name":"Gunnar Hovik","display_name":"guno327","email":"gunnar.hovik@canonical.com","username":"guno327"},"change_message_id":"bc40fe3c4dc7cfaead61d78e602ce1e041c96926","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":13,"id":"07fa3847_5082171e","updated":"2026-06-09 16:23:05.000000000","message":"Latest patchset implements the feature as just the resource and analyzes the uploaded archive to determine all other values.\n\nIt also adds some checks that the theme is in a plausible format as well as book-keeping to ensure not re-updating the theme when assets are already up to date in the container since we are running analysis on the files to extract the needed information already.","commit_id":"dd93f45538f80ed5a8887a2e3f2a0fe2ac709fe9"},{"author":{"_account_id":8992,"name":"Billy Olsen","email":"billy.olsen@canonical.com","username":"billy-olsen"},"change_message_id":"616393ef2e4c1127e383a1d295ec6dbd2bd6d842","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":14,"id":"58129955_8284dac4","updated":"2026-06-09 21:27:43.000000000","message":"Thank you for resolving all concerns raised @gunnar.hovik@canonical.com","commit_id":"9cdf6564f3979c5f4667f3f14651b994959c174b"}],"charms/horizon-k8s/charmcraft.yaml":[{"author":{"_account_id":35761,"name":"Guillaume Boutry","display_name":"gboutry","email":"guillaume.boutry@canonical.com","username":"gboutry"},"change_message_id":"a8b0fa02cc1626ba9e9200de3e8d1d787476e296","unresolved":true,"context_lines":[{"line_number":5,"context_line":"  OpenStack Horizon provides an HTTP service for managing, selecting,"},{"line_number":6,"context_line":"  and claiming providers of classes of inventory representing available"},{"line_number":7,"context_line":"  resources in a cloud."},{"line_number":8,"context_line":"  ."},{"line_number":9,"context_line":"assumes:"},{"line_number":10,"context_line":"  - k8s-api"},{"line_number":11,"context_line":"  - juju \u003e\u003d 3.1"}],"source_content_type":"text/x-yaml","patch_set":3,"id":"ccb1f072_4d380fae","side":"PARENT","line":8,"updated":"2026-05-05 09:03:25.000000000","message":"Can we not have unrelated change as part of this review?","commit_id":"acaecd7130250fc1bbc88d42f800cbc8760bf7ef"},{"author":{"_account_id":39104,"name":"Gunnar Hovik","display_name":"guno327","email":"gunnar.hovik@canonical.com","username":"guno327"},"change_message_id":"031762a77bd4e08a0fd446c9685d675dcd88ee58","unresolved":false,"context_lines":[{"line_number":5,"context_line":"  OpenStack Horizon provides an HTTP service for managing, selecting,"},{"line_number":6,"context_line":"  and claiming providers of classes of inventory representing available"},{"line_number":7,"context_line":"  resources in a cloud."},{"line_number":8,"context_line":"  ."},{"line_number":9,"context_line":"assumes:"},{"line_number":10,"context_line":"  - k8s-api"},{"line_number":11,"context_line":"  - juju \u003e\u003d 3.1"}],"source_content_type":"text/x-yaml","patch_set":3,"id":"36f4686f_5a6bd735","side":"PARENT","line":8,"in_reply_to":"ccb1f072_4d380fae","updated":"2026-05-05 12:08:40.000000000","message":"Must have been my auto-format on file save. Will remove on next patchset after path forward is decided.","commit_id":"acaecd7130250fc1bbc88d42f800cbc8760bf7ef"},{"author":{"_account_id":35761,"name":"Guillaume Boutry","display_name":"gboutry","email":"guillaume.boutry@canonical.com","username":"gboutry"},"change_message_id":"a8b0fa02cc1626ba9e9200de3e8d1d787476e296","unresolved":true,"context_lines":[{"line_number":28,"context_line":"    session-timeout:"},{"line_number":29,"context_line":"      type: int"},{"line_number":30,"context_line":"      default: 3600"},{"line_number":31,"context_line":"      description: |"},{"line_number":32,"context_line":"        A method to supersede the token timeout with a shorter dashboard"},{"line_number":33,"context_line":"        session timeout in seconds. For example, if your token expires in 60 minutes,"},{"line_number":34,"context_line":"        a value of 1800 will log users out after 30 minutes."}],"source_content_type":"text/x-yaml","patch_set":3,"id":"3e01f1ad_0b69fd71","line":31,"updated":"2026-05-05 09:03:25.000000000","message":"Same","commit_id":"09a2306c03eff5bd5727cb4b9a951cc0c3149733"},{"author":{"_account_id":39104,"name":"Gunnar Hovik","display_name":"guno327","email":"gunnar.hovik@canonical.com","username":"guno327"},"change_message_id":"031762a77bd4e08a0fd446c9685d675dcd88ee58","unresolved":false,"context_lines":[{"line_number":28,"context_line":"    session-timeout:"},{"line_number":29,"context_line":"      type: int"},{"line_number":30,"context_line":"      default: 3600"},{"line_number":31,"context_line":"      description: |"},{"line_number":32,"context_line":"        A method to supersede the token timeout with a shorter dashboard"},{"line_number":33,"context_line":"        session timeout in seconds. For example, if your token expires in 60 minutes,"},{"line_number":34,"context_line":"        a value of 1800 will log users out after 30 minutes."}],"source_content_type":"text/x-yaml","patch_set":3,"id":"845347f2_1710e2be","line":31,"in_reply_to":"3e01f1ad_0b69fd71","updated":"2026-05-05 12:08:40.000000000","message":"Same as above","commit_id":"09a2306c03eff5bd5727cb4b9a951cc0c3149733"},{"author":{"_account_id":35761,"name":"Guillaume Boutry","display_name":"gboutry","email":"guillaume.boutry@canonical.com","username":"gboutry"},"change_message_id":"a8b0fa02cc1626ba9e9200de3e8d1d787476e296","unresolved":true,"context_lines":[{"line_number":195,"context_line":"      description: |"},{"line_number":196,"context_line":"        This option can be used to disable the inclusion of upstream default horizon themes"},{"line_number":197,"context_line":"        (default + material)."},{"line_number":198,"context_line":"    include-ubuntu-theme:"},{"line_number":199,"context_line":"      type: boolean"},{"line_number":200,"context_line":"      default: true"},{"line_number":201,"context_line":"      description: |"},{"line_number":202,"context_line":"        This option can be used to disable the inclusion of the default Ubuntu theme."},{"line_number":203,"context_line":"    default-theme:"},{"line_number":204,"context_line":"      type: string"},{"line_number":205,"context_line":"      default: \"ubuntu\""}],"source_content_type":"text/x-yaml","patch_set":3,"id":"d4a87e53_d4303255","line":202,"range":{"start_line":198,"start_character":4,"end_line":202,"end_character":84},"updated":"2026-05-05 09:03:25.000000000","message":"Is there a specific reason we want to disable the ubuntu theme for being selectable?","commit_id":"09a2306c03eff5bd5727cb4b9a951cc0c3149733"},{"author":{"_account_id":39104,"name":"Gunnar Hovik","display_name":"guno327","email":"gunnar.hovik@canonical.com","username":"guno327"},"change_message_id":"bc40fe3c4dc7cfaead61d78e602ce1e041c96926","unresolved":false,"context_lines":[{"line_number":195,"context_line":"      description: |"},{"line_number":196,"context_line":"        This option can be used to disable the inclusion of upstream default horizon themes"},{"line_number":197,"context_line":"        (default + material)."},{"line_number":198,"context_line":"    include-ubuntu-theme:"},{"line_number":199,"context_line":"      type: boolean"},{"line_number":200,"context_line":"      default: true"},{"line_number":201,"context_line":"      description: |"},{"line_number":202,"context_line":"        This option can be used to disable the inclusion of the default Ubuntu theme."},{"line_number":203,"context_line":"    default-theme:"},{"line_number":204,"context_line":"      type: string"},{"line_number":205,"context_line":"      default: \"ubuntu\""}],"source_content_type":"text/x-yaml","patch_set":3,"id":"279fe8b2_81634551","line":202,"range":{"start_line":198,"start_character":4,"end_line":202,"end_character":84},"in_reply_to":"a33e5a90_7bc92344","updated":"2026-06-09 16:23:05.000000000","message":"Done","commit_id":"09a2306c03eff5bd5727cb4b9a951cc0c3149733"},{"author":{"_account_id":39104,"name":"Gunnar Hovik","display_name":"guno327","email":"gunnar.hovik@canonical.com","username":"guno327"},"change_message_id":"031762a77bd4e08a0fd446c9685d675dcd88ee58","unresolved":true,"context_lines":[{"line_number":195,"context_line":"      description: |"},{"line_number":196,"context_line":"        This option can be used to disable the inclusion of upstream default horizon themes"},{"line_number":197,"context_line":"        (default + material)."},{"line_number":198,"context_line":"    include-ubuntu-theme:"},{"line_number":199,"context_line":"      type: boolean"},{"line_number":200,"context_line":"      default: true"},{"line_number":201,"context_line":"      description: |"},{"line_number":202,"context_line":"        This option can be used to disable the inclusion of the default Ubuntu theme."},{"line_number":203,"context_line":"    default-theme:"},{"line_number":204,"context_line":"      type: string"},{"line_number":205,"context_line":"      default: \"ubuntu\""}],"source_content_type":"text/x-yaml","patch_set":3,"id":"a33e5a90_7bc92344","line":202,"range":{"start_line":198,"start_character":4,"end_line":202,"end_character":84},"in_reply_to":"d4a87e53_d4303255","updated":"2026-05-05 12:08:40.000000000","message":"For completeness. The purpose of custom themeing is for end-users who will be handing off access to their openstack instances to their own clients and do not want any branding affiliated with canonical/ubuntu. They may not want the ubuntu theme selectable to them.\n\nEven with that rational aside, it is an easy toggle to enable; why not give the end-user more control?","commit_id":"09a2306c03eff5bd5727cb4b9a951cc0c3149733"},{"author":{"_account_id":35761,"name":"Guillaume Boutry","display_name":"gboutry","email":"guillaume.boutry@canonical.com","username":"gboutry"},"change_message_id":"91efc2d2240c788bd41c12d68ab6a634bbe12b8c","unresolved":true,"context_lines":[{"line_number":233,"context_line":"    filename: theme.tar.gz"},{"line_number":234,"context_line":"    description: |"},{"line_number":235,"context_line":"      A tarball containg that custom theme assets to be used for the dashboard."},{"line_number":236,"context_line":"      The tarball should be formed at the root of the theme folder, f.e. "},{"line_number":237,"context_line":"      `tar -czf theme.tar.gz -C /path/to/your/custom_theme`."},{"line_number":238,"context_line":""},{"line_number":239,"context_line":"requires:"}],"source_content_type":"text/x-yaml","patch_set":9,"id":"f18f548a_10ab7df1","line":236,"range":{"start_line":236,"start_character":72,"end_line":236,"end_character":73},"updated":"2026-05-19 08:24:48.000000000","message":"White space","commit_id":"12064d6957e2d1dd6474c8c16af31f0e3d1dd6a0"},{"author":{"_account_id":39104,"name":"Gunnar Hovik","display_name":"guno327","email":"gunnar.hovik@canonical.com","username":"guno327"},"change_message_id":"a5d9bb3838a67fae0e976a1d39d19f3281700dfd","unresolved":false,"context_lines":[{"line_number":233,"context_line":"    filename: theme.tar.gz"},{"line_number":234,"context_line":"    description: |"},{"line_number":235,"context_line":"      A tarball containg that custom theme assets to be used for the dashboard."},{"line_number":236,"context_line":"      The tarball should be formed at the root of the theme folder, f.e. "},{"line_number":237,"context_line":"      `tar -czf theme.tar.gz -C /path/to/your/custom_theme`."},{"line_number":238,"context_line":""},{"line_number":239,"context_line":"requires:"}],"source_content_type":"text/x-yaml","patch_set":9,"id":"d1745239_04d03d8e","line":236,"range":{"start_line":236,"start_character":72,"end_line":236,"end_character":73},"in_reply_to":"f18f548a_10ab7df1","updated":"2026-05-19 14:46:01.000000000","message":"Done","commit_id":"12064d6957e2d1dd6474c8c16af31f0e3d1dd6a0"}],"charms/horizon-k8s/src/charm.py":[{"author":{"_account_id":35761,"name":"Guillaume Boutry","display_name":"gboutry","email":"guillaume.boutry@canonical.com","username":"gboutry"},"change_message_id":"a8b0fa02cc1626ba9e9200de3e8d1d787476e296","unresolved":true,"context_lines":[{"line_number":216,"context_line":"        \"\"\"Retrieve the URL for the Horizon OpenStack Dashboard.\"\"\""},{"line_number":217,"context_line":"        event.set_results({\"url\": self.public_url})"},{"line_number":218,"context_line":""},{"line_number":219,"context_line":"    def _extract_custom_theme(self) -\u003e None:"},{"line_number":220,"context_line":"        \"\"\"Extract custom theme into the container and update assets.\"\"\""},{"line_number":221,"context_line":"        container \u003d self.unit.get_container(self.service_name)"},{"line_number":222,"context_line":"        if not container.can_connect():"}],"source_content_type":"text/x-python","patch_set":3,"id":"6381c429_a3edade8","line":219,"updated":"2026-05-05 09:03:25.000000000","message":"How do you expect to get this file into the container then?","commit_id":"09a2306c03eff5bd5727cb4b9a951cc0c3149733"},{"author":{"_account_id":39104,"name":"Gunnar Hovik","display_name":"guno327","email":"gunnar.hovik@canonical.com","username":"guno327"},"change_message_id":"031762a77bd4e08a0fd446c9685d675dcd88ee58","unresolved":true,"context_lines":[{"line_number":216,"context_line":"        \"\"\"Retrieve the URL for the Horizon OpenStack Dashboard.\"\"\""},{"line_number":217,"context_line":"        event.set_results({\"url\": self.public_url})"},{"line_number":218,"context_line":""},{"line_number":219,"context_line":"    def _extract_custom_theme(self) -\u003e None:"},{"line_number":220,"context_line":"        \"\"\"Extract custom theme into the container and update assets.\"\"\""},{"line_number":221,"context_line":"        container \u003d self.unit.get_container(self.service_name)"},{"line_number":222,"context_line":"        if not container.can_connect():"}],"source_content_type":"text/x-python","patch_set":3,"id":"68c3184e_c0e6e6d4","line":219,"in_reply_to":"6381c429_a3edade8","updated":"2026-05-05 12:08:40.000000000","message":"I am not sure what the question here is. This code calls `container.push` to upload the archive and then uses `container.exec` to extract it inside of the container. The file is at no point on the host/controller system. It gets decoded straight from base64 stored as config option into the container at configure time.","commit_id":"09a2306c03eff5bd5727cb4b9a951cc0c3149733"},{"author":{"_account_id":39104,"name":"Gunnar Hovik","display_name":"guno327","email":"gunnar.hovik@canonical.com","username":"guno327"},"change_message_id":"a5d9bb3838a67fae0e976a1d39d19f3281700dfd","unresolved":false,"context_lines":[{"line_number":216,"context_line":"        \"\"\"Retrieve the URL for the Horizon OpenStack Dashboard.\"\"\""},{"line_number":217,"context_line":"        event.set_results({\"url\": self.public_url})"},{"line_number":218,"context_line":""},{"line_number":219,"context_line":"    def _extract_custom_theme(self) -\u003e None:"},{"line_number":220,"context_line":"        \"\"\"Extract custom theme into the container and update assets.\"\"\""},{"line_number":221,"context_line":"        container \u003d self.unit.get_container(self.service_name)"},{"line_number":222,"context_line":"        if not container.can_connect():"}],"source_content_type":"text/x-python","patch_set":3,"id":"75b8c33a_2b8235cd","line":219,"in_reply_to":"68c3184e_c0e6e6d4","updated":"2026-05-19 14:46:01.000000000","message":"Done","commit_id":"09a2306c03eff5bd5727cb4b9a951cc0c3149733"},{"author":{"_account_id":10366,"name":"Hemanth N","email":"hemanth.nakkina@canonical.com","username":"Hemanth"},"change_message_id":"956cadaa79906f5bebca513a6574470591173b62","unresolved":true,"context_lines":[{"line_number":143,"context_line":""},{"line_number":144,"context_line":"def update_horizon_assets(container: ops.Container):"},{"line_number":145,"context_line":"    \"\"\"Regenerate django templates for horizon.\"\"\""},{"line_number":146,"context_line":"    logger.debug(\"Re-generating horizon assets\")"},{"line_number":147,"context_line":"    manage \u003d \"/usr/share/openstack-dashboard/manage.py\""},{"line_number":148,"context_line":"    exec("},{"line_number":149,"context_line":"        container,"}],"source_content_type":"text/x-python","patch_set":11,"id":"689ea87d_22e9c85e","line":146,"updated":"2026-06-04 08:41:33.000000000","message":"Is regeneration of horizon assets for every event OK? Or do we need a mechanism to check if custom theme tar is not changed, do not run this step?","commit_id":"2be973d3659b5512b7f4740c7459f920d05894ed"},{"author":{"_account_id":39104,"name":"Gunnar Hovik","display_name":"guno327","email":"gunnar.hovik@canonical.com","username":"guno327"},"change_message_id":"bc40fe3c4dc7cfaead61d78e602ce1e041c96926","unresolved":false,"context_lines":[{"line_number":143,"context_line":""},{"line_number":144,"context_line":"def update_horizon_assets(container: ops.Container):"},{"line_number":145,"context_line":"    \"\"\"Regenerate django templates for horizon.\"\"\""},{"line_number":146,"context_line":"    logger.debug(\"Re-generating horizon assets\")"},{"line_number":147,"context_line":"    manage \u003d \"/usr/share/openstack-dashboard/manage.py\""},{"line_number":148,"context_line":"    exec("},{"line_number":149,"context_line":"        container,"}],"source_content_type":"text/x-python","patch_set":11,"id":"2ad5eee5_a2fc954b","line":146,"in_reply_to":"65721c0d_c3c0df6a","updated":"2026-06-09 16:23:05.000000000","message":"Done","commit_id":"2be973d3659b5512b7f4740c7459f920d05894ed"},{"author":{"_account_id":39104,"name":"Gunnar Hovik","display_name":"guno327","email":"gunnar.hovik@canonical.com","username":"guno327"},"change_message_id":"5beb7feef43633cd2728b7bbd9c3cc3c12dcbfd9","unresolved":true,"context_lines":[{"line_number":143,"context_line":""},{"line_number":144,"context_line":"def update_horizon_assets(container: ops.Container):"},{"line_number":145,"context_line":"    \"\"\"Regenerate django templates for horizon.\"\"\""},{"line_number":146,"context_line":"    logger.debug(\"Re-generating horizon assets\")"},{"line_number":147,"context_line":"    manage \u003d \"/usr/share/openstack-dashboard/manage.py\""},{"line_number":148,"context_line":"    exec("},{"line_number":149,"context_line":"        container,"}],"source_content_type":"text/x-python","patch_set":11,"id":"65721c0d_c3c0df6a","line":146,"in_reply_to":"689ea87d_22e9c85e","updated":"2026-06-04 16:31:43.000000000","message":"Why would this not be OK? In my testing this takes little time to run. Juju hooks are reactive by nature. If the configure container method is being fired than something changed, something that could have affected the state of the container resources. I would much rather be safe than sorry when it comes to ensuring that the assets are up to date.\n\nAdditionally, implementing this kind of check would require a method for determining the current state of the assets *in* the container (not just what juju assumes that state is). Since this is a k8s pod any number of operations could have occurred that caused the pod to be destroyed and re-instantiated (this would trigger the configure container method and thus this work ensuring that despite a completely new container it is up to date with the correct resources). \n\nThe extra work to query the running container and somehow compare the existing state to the expected not only seems against the design philosophy of reactive charms, but hardly worth the pay-off (potentially cutting a negligible amount of time from *some* hook runs) for the added complexity and opportunity for accidental state drift.","commit_id":"2be973d3659b5512b7f4740c7459f920d05894ed"},{"author":{"_account_id":10366,"name":"Hemanth N","email":"hemanth.nakkina@canonical.com","username":"Hemanth"},"change_message_id":"956cadaa79906f5bebca513a6574470591173b62","unresolved":true,"context_lines":[{"line_number":238,"context_line":"            theme_dir \u003d f\"/usr/share/openstack-dashboard/openstack_dashboard/themes/{theme_name}\""},{"line_number":239,"context_line":"            with open(theme_path, \"rb\") as theme:"},{"line_number":240,"context_line":"                container.push(archive_path, theme)"},{"line_number":241,"context_line":"            container.exec([\"mkdir\", \"-p\", theme_dir]).wait_output()"},{"line_number":242,"context_line":"            container.exec("},{"line_number":243,"context_line":"                [\"tar\", \"-xzf\", archive_path, \"-C\", theme_dir]"},{"line_number":244,"context_line":"            ).wait_output()"}],"source_content_type":"text/x-python","patch_set":11,"id":"6758bbc7_3cd7277b","line":241,"updated":"2026-06-04 08:41:33.000000000","message":"Can you catch ops.pebble.ExecError for container.exec calls","commit_id":"2be973d3659b5512b7f4740c7459f920d05894ed"},{"author":{"_account_id":39104,"name":"Gunnar Hovik","display_name":"guno327","email":"gunnar.hovik@canonical.com","username":"guno327"},"change_message_id":"5beb7feef43633cd2728b7bbd9c3cc3c12dcbfd9","unresolved":false,"context_lines":[{"line_number":238,"context_line":"            theme_dir \u003d f\"/usr/share/openstack-dashboard/openstack_dashboard/themes/{theme_name}\""},{"line_number":239,"context_line":"            with open(theme_path, \"rb\") as theme:"},{"line_number":240,"context_line":"                container.push(archive_path, theme)"},{"line_number":241,"context_line":"            container.exec([\"mkdir\", \"-p\", theme_dir]).wait_output()"},{"line_number":242,"context_line":"            container.exec("},{"line_number":243,"context_line":"                [\"tar\", \"-xzf\", archive_path, \"-C\", theme_dir]"},{"line_number":244,"context_line":"            ).wait_output()"}],"source_content_type":"text/x-python","patch_set":11,"id":"d070556a_7bdb31af","line":241,"in_reply_to":"6758bbc7_3cd7277b","updated":"2026-06-04 16:31:43.000000000","message":"I did you one better and used y\u0027all\u0027s `exec` method which handles the errors internally. Why I was not already doing this, I do not know; that would be a question for me a month ago.","commit_id":"2be973d3659b5512b7f4740c7459f920d05894ed"}]}
