1818import pathlib
1919
2020from . import model , tag , utils , jasyncio
21+ from .url import URL , Schema
2122from .status import derive_status
2223from .annotationhelper import _get_annotations , _set_annotations
2324from .client import client
2425from .errors import JujuError , JujuApplicationConfigError
2526from .bundle import get_charm_series
2627from .placement import parse as parse_placement
28+ from .origin import Channel
2729
2830log = logging .getLogger (__name__ )
2931
@@ -609,98 +611,147 @@ async def refresh(
609611 :param str switch: Crossgrade charm url
610612
611613 """
612- if path is not None :
613- await self .local_refresh (channel , force , force_series , force_units ,
614- path , resources )
615- return
616614 if resources is not None :
617615 raise NotImplementedError ("resources option is not implemented" )
618616
619617 if switch is not None and revision is not None :
620618 raise ValueError ("switch and revision are mutually exclusive" )
621619
622- client_facade = client .ClientFacade .from_connection (self .connection )
623- resources_facade = client .ResourcesFacade .from_connection (
624- self .connection )
625620 app_facade = self ._facade ()
621+ resources_facade = client .ResourcesFacade .from_connection (self .connection )
622+ charms_facade = client .CharmsFacade .from_connection (self .connection )
626623
627- charmstore = self .model .charmstore
628- charmstore_entity = None
624+ # 1 - Figure out the destination origin and destination charm_url
625+ # 2 - Then take care of the resources
626+ # 3 - Finally execute the upgrade
629627
630- if switch is not None :
631- charm_url = switch
632- if not charm_url .startswith ('cs:' ):
633- charm_url = 'cs:' + charm_url
634- else :
635- charm_url = self .data ['charm-url' ]
636- charm_url = charm_url .rpartition ('-' )[0 ]
637- if revision is not None :
638- charm_url = "%s-%d" % (charm_url , revision )
639- else :
640- charmstore_entity = await charmstore .entity (charm_url ,
641- channel = channel )
642- charm_url = charmstore_entity ['Id' ]
628+ # Get the charm URL and charm origin of the given application is running at present.
629+ charm_url_origin_result = await app_facade .GetCharmURLOrigin (application = self .name )
630+ if charm_url_origin_result .error is not None :
631+ err = charm_url_origin_result .error
632+ raise JujuError ("%s : %s" % (err .code , err .message ))
633+ charm_url = switch or charm_url_origin_result .url
634+ origin = charm_url_origin_result .charm_origin
643635
644- if charm_url == self .data ['charm-url' ]:
645- raise JujuError ('already running charm "%s"' % charm_url )
636+ if path is not None :
637+ await self .local_refresh (origin , force , force_series ,
638+ force_units , path , resources )
639+ return
646640
647- # Update charm
648- await client_facade . AddCharm (
649- url = charm_url ,
650- force = force ,
651- channel = channel
652- )
641+ parsed_url = URL . parse ( charm_url )
642+ charm_name = parsed_url . name
643+
644+ if parsed_url . schema is None :
645+ raise JujuError ( "A ch: or cs: schema is required for application "
646+ "refresh, given : %s " % str ( parsed_url ) )
653647
654- # Update resources
655- if not charmstore_entity :
656- charmstore_entity = await charmstore .entity (charm_url ,
657- channel = channel )
658- store_resources = charmstore_entity ['Meta' ]['resources' ]
648+ if revision is not None :
649+ origin .revision = revision
659650
651+ # Make the source-specific changes to the origin/channel/url
652+ # (and also get the resources necessary to deploy the (destination) charm -- for later)
653+ if Schema .CHARM_HUB .matches (parsed_url .schema ):
654+ origin .source = 'charm-hub'
655+ if channel :
656+ ch = Channel .parse (channel ).normalize ()
657+ origin .risk = ch .risk
658+ origin .track = ch .track
659+
660+ charmhub = self .model .charmhub
661+ charm_resources = await charmhub .list_resources (charm_name )
662+ else :
663+ charmstore = self .model .charmstore
664+ charmstore_entity = None
665+ if switch is None :
666+ charm_url = charm_url .rpartition ('-' )[0 ]
667+ if revision is not None :
668+ charm_url = "%s-%d" % (charm_url , revision )
669+ else :
670+ charmstore_entity = await charmstore .entity (charm_url , channel = channel )
671+ charm_url = charmstore_entity ['Id' ]
672+ origin .source = 'charm-store'
673+ if channel :
674+ origin .risk = channel
675+ if charmstore_entity is None :
676+ charmstore_entity = await charmstore .entity (charm_url , channel = channel )
677+ charm_resources = charmstore_entity ['Meta' ]['resources' ]
678+
679+ # Resolve the given charm URLs with an optionally specified preferred channel.
680+ # Channel provided via CharmOrigin.
681+ resolved_charm_with_channel_results = await charms_facade .ResolveCharms (
682+ resolve = [client .ResolveCharmWithChannel (
683+ charm_origin = origin ,
684+ switch_charm = True if switch else False , # rpc expects boolean type
685+ reference = charm_url ,
686+ )])
687+ resolved_charm = resolved_charm_with_channel_results .results [0 ]
688+
689+ # Get the destination origin and destination charm_url from the resolved charm
690+ if resolved_charm .error is not None :
691+ err = resolved_charm .error
692+ raise JujuError ("%s : %s" % (err .code , err .message ))
693+ dest_origin = resolved_charm .charm_origin
694+ charm_url = resolved_charm .url
695+
696+ # Add the charm with the destination url and origin
697+ charm_origin_result = await charms_facade .AddCharm (url = charm_url ,
698+ force = force ,
699+ charm_origin = dest_origin )
700+ if charm_origin_result .error is not None :
701+ err = charm_origin_result .error
702+ raise JujuError ("%s : %s" % (err .code , err .message ))
703+
704+ # Now take care of the resources:
705+
706+ # Already prepped the charm_resources
707+ # Now get the existing resources from the ResourcesFacade
660708 request_data = [client .Entity (self .tag )]
661709 response = await resources_facade .ListResources (entities = request_data )
662710 existing_resources = {
663711 resource .name : resource
664712 for resource in response .results [0 ].resources
665713 }
666714
715+ # Compute the difference btw resources needed and the existing resources
667716 resources_to_update = [
668- resource for resource in store_resources
669- if resource [ 'Name' ] not in existing_resources or
670- existing_resources [resource [ 'Name' ] ].origin != 'upload'
717+ resource for resource in charm_resources
718+ if resource . get ( 'Name' , resource . get ( 'name' )) not in existing_resources or
719+ existing_resources [resource . get ( 'Name' , resource . get ( 'name' )) ].origin != 'upload'
671720 ]
672721
722+ # Update the resources
673723 if resources_to_update :
674- request_data = [
675- client .CharmResource (
676- description = resource .get ('Description' ),
677- fingerprint = resource ['Fingerprint' ],
678- name = resource ['Name' ],
679- path = resource ['Path' ],
680- revision = resource ['Revision' ],
681- size = resource ['Size' ],
682- type_ = resource ['Type' ],
724+ request_data = []
725+ for resource in resources_to_update :
726+ request_data .append (client .CharmResource (
727+ description = resource .get ('Description' , resource .get ('description' )),
728+ fingerprint = resource .get ('Fingerprint' , resource .get ('fingerprint' )),
729+ name = resource .get ('Name' , resource .get ('name' )),
730+ path = resource .get ('Path' , resource .get ('filename' )),
731+ revision = resource .get ('Revision' , resource .get ('revision' , - 1 )),
732+ size = resource .get ('Size' , resource .get ('size' )),
733+ type_ = resource .get ('Type' , resource .get ('type' )),
683734 origin = 'store' ,
684- ) for resource in resources_to_update
685- ]
735+ ))
686736 response = await resources_facade .AddPendingResources (
687737 application_tag = self .tag ,
688738 charm_url = charm_url ,
689- resources = request_data
739+ resources = request_data ,
740+ charm_origin = dest_origin ,
690741 )
691742 pending_ids = response .pending_ids
692743 resource_ids = {
693- resource [ 'Name' ] : id
744+ resource . get ( 'Name' , resource . get ( 'name' )) : id
694745 for resource , id in zip (resources_to_update , pending_ids )
695746 }
696747 else :
697748 resource_ids = None
698749
699- # Update application
750+ # Update the application
700751 await app_facade .SetCharm (
701752 application = self .entity_id ,
702- channel = channel ,
703753 charm_url = charm_url ,
754+ charm_origin = dest_origin ,
704755 config_settings = None ,
705756 config_settings_yaml = None ,
706757 force = force ,
@@ -717,7 +768,8 @@ async def refresh(
717768 upgrade_charm = refresh
718769
719770 async def local_refresh (
720- self , channel = None , force = False , force_series = False , force_units = False ,
771+ self , charm_origin = None , force = False , force_series = False ,
772+ force_units = False ,
721773 path = None , resources = None ):
722774 """Refresh the charm for this application with a local charm.
723775
@@ -760,7 +812,7 @@ async def local_refresh(
760812 # Update application
761813 await app_facade .SetCharm (
762814 application = self .entity_id ,
763- channel = channel ,
815+ charm_origin = charm_origin ,
764816 charm_url = charm_url ,
765817 config_settings = None ,
766818 config_settings_yaml = None ,
0 commit comments