@@ -651,40 +651,59 @@ def push_from_dir(
651651 cname : str ,
652652 directory : str ,
653653 manifest_file : str ,
654- commit : Optional [ str ] = None ,
654+ additional_tags : list = None ,
655655 ):
656- # Step 1 scan and extract nested artifacts:
657- for file in os .listdir (directory ):
658- try :
659- if file .endswith (".pxe.tar.gz" ):
660- logger .info (f"Found nested artifact { file } " )
661- nested_tar_obj = tarfile .open (f"{ directory } /{ file } " )
662- nested_tar_obj .extractall (filter = "data" , path = directory )
663- nested_tar_obj .close ()
664- except (OSError , tarfile .FilterError , tarfile .TarError ) as e :
665- print (f"Failed to extract nested artifact { file } " , e )
666- exit (1 )
656+ """
657+ Push artifacts from a directory to a registry
658+
659+ Args:
660+ architecture: Target architecture of the image
661+ version: Version tag for the image
662+ cname: Canonical name of the image
663+ directory: Directory containing the artifacts
664+ manifest_file: File to write the manifest index entry to
665+ additional_tags: Additional tags to push the manifest with
666+
667+ Returns:
668+ The digest of the pushed manifest
669+ """
670+ if additional_tags is None :
671+ additional_tags = []
667672
668673 try :
674+ # Step 1: scan and extract nested artifacts
675+ for file in os .listdir (directory ):
676+ try :
677+ if file .endswith (".pxe.tar.gz" ):
678+ logger .info (f"Found nested artifact { file } " )
679+ nested_tar_obj = tarfile .open (f"{ directory } /{ file } " )
680+ nested_tar_obj .extractall (filter = "data" , path = directory )
681+ nested_tar_obj .close ()
682+ except (OSError , tarfile .FilterError , tarfile .TarError ) as e :
683+ print (f"Failed to extract nested artifact { file } " , e )
684+ exit (1 )
685+
686+ # Step 2: Get metadata from files
669687 oci_metadata = get_oci_metadata_from_fileset (
670688 os .listdir (directory ), architecture
671689 )
672690
673691 features = ""
692+ commit = ""
674693 for artifact in oci_metadata :
675694 if artifact ["media_type" ] == "application/io.gardenlinux.release" :
676- file = open (f"{ directory } /{ artifact ["file_name" ]} " , "r" )
677- lines = file . readlines ()
678- for line in lines :
679- if line . strip () .startswith ("GARDENLINUX_FEATURES=" ):
680- features = line .strip (). removeprefix (
681- "GARDENLINUX_FEATURES="
682- )
683- break
684- file . close ()
685-
686- flavor = get_flavor_from_cname ( cname , get_arch = True )
687-
695+ with open (f"{ directory } /{ artifact ["file_name" ]} " , "r" ) as file :
696+ for line in file :
697+ line = line . strip ()
698+ if line .startswith ("GARDENLINUX_FEATURES=" ):
699+ features = line .removeprefix ("GARDENLINUX_FEATURES=" )
700+ elif line . startswith ( "GARDENLINUX_COMMIT_ID=" ):
701+ commit = line . removeprefix ( "GARDENLINUX_COMMIT_ID=" )
702+ if features and commit : # Break if both values are found
703+ break
704+ break # Break after processing the release file
705+
706+ # Step 3: Push the image manifest
688707 digest = self .push_image_manifest (
689708 architecture ,
690709 cname ,
@@ -695,11 +714,153 @@ def push_from_dir(
695714 manifest_file ,
696715 commit = commit ,
697716 )
717+
718+ # Step 4: Process additional tags if provided
719+ if additional_tags and len (additional_tags ) > 0 :
720+ print (f"DEBUG: Processing { len (additional_tags )} additional tags" )
721+ logger .info (f"Processing { len (additional_tags )} additional tags" )
722+
723+ # Call push_additional_tags with repository information
724+ self .push_additional_tags (
725+ architecture ,
726+ cname ,
727+ version ,
728+ additional_tags ,
729+ repo_name = None ,
730+ registry_url = None
731+ )
732+
733+ return digest
698734 except Exception as e :
699735 print ("Error: " , e )
700736 exit (1 )
701- return digest
702737
738+ def push_additional_tags (self , architecture , cname , version , additional_tags , repo_name = None , registry_url = None ):
739+ """
740+ Push additional tags for an existing manifest using requests directly
741+
742+ Args:
743+ architecture: Target architecture of the image
744+ cname: Canonical name of the image
745+ version: Version tag for the image
746+ additional_tags: List of additional tags to push
747+ repo_name: Repository name (optional, will be determined if not provided)
748+ registry_url: Registry URL (optional, will be determined if not provided)
749+ """
750+ try :
751+ print (f"DEBUG: Processing { len (additional_tags )} additional tags" )
752+
753+ # Source tag is version-cname-architecture
754+ source_tag = f"{ version } -{ cname } -{ architecture } "
755+
756+ # Use provided repo_name or determine it from container_name
757+ if repo_name is None :
758+ # Get container parts
759+ container_parts = self .container_name .split (':' )
760+ container_repo = container_parts [0 ]
761+
762+ # Parse repository name
763+ if '/' in container_repo :
764+ # Production environment
765+ repo_parts = container_repo .split ('/' )
766+ repo_name = '/' .join (repo_parts [1 :])
767+ else :
768+ # Test environment
769+ repo_name = "gardenlinux-example"
770+
771+ # Use provided registry_url or get from class
772+ if registry_url is None :
773+ # Check if we have a registry_url property
774+ if hasattr (self , 'registry_url' ) and self .registry_url :
775+ registry_url = self .registry_url
776+ else :
777+ # Determine if we should use HTTP or HTTPS
778+ use_insecure = getattr (self , 'insecure' , True ) # Default to True for tests
779+ protocol = "http" if use_insecure else "https"
780+
781+ # Get registry host
782+ container_parts = self .container_name .split (':' )
783+ container_repo = container_parts [0 ]
784+
785+ if '/' in container_repo :
786+ registry_host = container_repo .split ('/' )[0 ]
787+ else :
788+ registry_host = "127.0.0.1:18081" # Default for test environment
789+
790+ registry_url = f"{ protocol } ://{ registry_host } "
791+
792+ # Ensure registry_url has protocol prefix
793+ if not registry_url .startswith ("http://" ) and not registry_url .startswith ("https://" ):
794+ use_insecure = getattr (self , 'insecure' , True ) # Default to True for tests
795+ protocol = "http" if use_insecure else "https"
796+ registry_url = f"{ protocol } ://{ registry_url } "
797+
798+ print (f"DEBUG: Using source tag: { source_tag } " )
799+ print (f"DEBUG: Using repository: { repo_name } " )
800+ print (f"DEBUG: Using registry URL: { registry_url } " )
801+
802+ # Import requests for HTTP operations
803+ import requests
804+
805+ # Set up authentication headers if needed
806+ headers = {}
807+ if hasattr (self , 'token' ) and self .token :
808+ headers ["Authorization" ] = f"Bearer { self .token } "
809+
810+ # Add accept headers for manifest formats
811+ headers .update ({
812+ "Accept" : "application/vnd.oci.image.manifest.v1+json,application/vnd.docker.distribution.manifest.v2+json"
813+ })
814+
815+ # Step 1: Get the manifest from the source tag
816+ source_url = f"{ registry_url } /v2/{ repo_name } /manifests/{ source_tag } "
817+ verify = not getattr (self , 'insecure' , True ) # Default to not verify for tests
818+
819+ # Get the source manifest
820+ source_resp = requests .get (source_url , headers = headers , verify = verify )
821+
822+ if source_resp .status_code != 200 :
823+ print (f"DEBUG: Failed to get source manifest: { source_resp .status_code } " )
824+ logger .error (f"Failed to get source manifest: { source_resp .status_code } " )
825+ return
826+
827+ # Get the manifest content and content-type
828+ manifest_content = source_resp .text
829+ content_type = source_resp .headers .get ("Content-Type" , "application/vnd.oci.image.manifest.v1+json" )
830+
831+ print (f"DEBUG: Successfully retrieved manifest with content type: { content_type } " )
832+
833+ # Step 2: For each additional tag, push the manifest
834+ for tag in additional_tags :
835+ try :
836+ print (f"DEBUG: Pushing additional tag: { tag } " )
837+
838+ # Push using requests
839+ push_headers = headers .copy ()
840+ push_headers .update ({
841+ "Content-Type" : content_type
842+ })
843+
844+ # Create push URL for the new tag
845+ tag_url = f"{ registry_url } /v2/{ repo_name } /manifests/{ tag } "
846+
847+ # Push the manifest using requests
848+ tag_resp = requests .put (tag_url , data = manifest_content , headers = push_headers , verify = verify )
849+
850+ if tag_resp .status_code in [200 , 201 ]:
851+ print (f"DEBUG: Successfully pushed tag { tag } " )
852+ logger .info (f"Successfully pushed tag { tag } " )
853+ else :
854+ print (f"DEBUG: Failed to push tag { tag } : { tag_resp .status_code } " )
855+ logger .error (f"Failed to push tag { tag } : { tag_resp .status_code } " )
856+
857+ except Exception as e :
858+ print (f"DEBUG: Error pushing tag { tag } : { str (e )} " )
859+ logger .error (f"Error pushing tag { tag } : { str (e )} " )
860+
861+ except Exception as e :
862+ print (f"DEBUG: Error in push_additional_tags: { str (e )} " )
863+ logger .error (f"Error in push_additional_tags: { str (e )} " )
703864
704865def extract_tar (tar : str , tmpdir : str ):
705866 """
0 commit comments