Comments (5)
From: @bmbouter (bmbouter)
Date: 2018-10-10T21:00:21Z
PR available at: #65
from pulp_ansible.
From: @bmbouter (bmbouter)
Date: 2018-11-16T14:18:42Z
This story was done a months ago, but it's currently blocked because of an on-going discussion with Ansible about adding a 'version' field to Roles.
from pulp_ansible.
From: @bmbouter (bmbouter)
Date: 2019-07-15T13:01:42Z
This PR had issues importing the same content twice because Pulp without a 'version' in the metadata can't recognize the tarball contains the same Role data. Adding a 'version' to the metadata field for role data would resolve this, but we need to work with the broader Galaxy community for that.
Here is a copy of the patch:
commit b0038261704c50f5b44423b65de8621ee4e6b536
Author: Brian Bouterse <[email protected]>
Date: Wed Oct 10 16:58:14 2018 -0400
Adds a one-shot upload
This one-shot upload will auto-discover any roles in the tarball
uploaded, create necessary Roles, and RoleVersion objects, and then
associate the RoleVersion objects with a new RepositoryVersion.
https://pulp.plan.io/issues/4066
closes #4066
diff --git a/README.rst b/README.rst
index 03fa20f..a7e7390 100644
--- a/README.rst
+++ b/README.rst
@@ -151,11 +151,35 @@ Look at the new Repository Version created
"number": 1
}
+Upload one or more Roles to Pulp (the easy way)
+-----------------------------------------------
-Upload a Role to Pulp
----------------------
+The upload API accepts a tarball which is opened up and any roles present will be imported and
+associated with the repository to create a new repository version.
-Download a role version.
+The created roles are assigned the following data:
+
+- The namespace is your username.
+- The role name is the role name of the directory in the uploaded tarball.
+- The version is an invented UUID due to version not being part of the Role metadata format. You can
+ assign versions later through the API.
+
+Here is a tarball with 6 roles in it.
+
+``curl -L https://github.com/pulp/ansible-pulp3/archive/master.tar.gz -o pulp.tar.gz``
+
+Upload it to Pulp and associate it with the repository:
+
+``http --form POST :8000/pulp_ansible/upload/ repository=$REPO_HREF [email protected] sha256=$(sha256sum pulp.tar.gz | awk '{ print $1 }')``
+
+
+Upload a Role to Pulp (the hard way)
+------------------------------------
+
+Uploading content this way lets you specify a namespace, name, and version which are automatically
+determined with the upload API above.
+
+To start "the hard way", download a role version.
``curl -L https://github.com/pulp/ansible-pulp3/archive/master.tar.gz -o pulp.tar.gz``
@@ -165,7 +189,7 @@ Create an Artifact by uploading the role version tarball to Pulp.
Create a Role content unit
---------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^
Create an Ansible role in Pulp.
@@ -173,7 +197,7 @@ Create an Ansible role in Pulp.
Create a ``role version`` from the Role and Artifact
------------------------------------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Create a content unit and point it to your Artifact and Role
@@ -181,13 +205,13 @@ Create a content unit and point it to your Artifact and Role
Add content to repository ``foo``
----------------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
``$ http POST ':8000'$REPO_HREF'versions/' add_content_units:="[\"$CONTENT_HREF\"]"``
Create a Publication
--------------------------------------------------
+--------------------
``$ http POST :8000/pulp/api/v3/ansible/publications/ repository=$REPO_HREF``
diff --git a/pulp_ansible/app/serializers.py b/pulp_ansible/app/serializers.py
index 7552559..addcae5 100644
--- a/pulp_ansible/app/serializers.py
+++ b/pulp_ansible/app/serializers.py
@@ -1,8 +1,10 @@
+from gettext import gettext as _
+
from rest_framework import serializers
from pulpcore.plugin.serializers import ContentSerializer, IdentityField, NestedIdentityField, \
RelatedField, RemoteSerializer
-from pulpcore.plugin.models import Artifact
+from pulpcore.plugin.models import Artifact, Repository
from .models import AnsibleRemote, AnsibleRole, AnsibleRoleVersion
@@ -61,3 +63,26 @@ class AnsibleRemoteSerializer(RemoteSerializer):
class Meta:
fields = RemoteSerializer.Meta.fields
model = AnsibleRemote
+
+
+class OneShotUploadSerializer(serializers.Serializer):
+ """
+ A serializer for the One Shot Upload API.
+ """
+
+ repository = serializers.HyperlinkedRelatedField(
+ help_text=_('A URI of the repository.'),
+ required=True,
+ queryset=Repository.objects.all(),
+ view_name='repositories-detail',
+ )
+
+ file = serializers.FileField(
+ help_text=_("The collection file."),
+ required=True,
+ )
+
+ sha256 = serializers.CharField(
+ required=False,
+ default=None,
+ )
diff --git a/pulp_ansible/app/tasks/__init__.py b/pulp_ansible/app/tasks/__init__.py
index 67e6aac..925e12a 100644
--- a/pulp_ansible/app/tasks/__init__.py
+++ b/pulp_ansible/app/tasks/__init__.py
@@ -1,2 +1,3 @@
+from .upload import import_content_from_tarball # noqa
from .synchronizing import synchronize # noqa
from .publishing import publish # noqa
diff --git a/pulp_ansible/app/tasks/upload.py b/pulp_ansible/app/tasks/upload.py
new file mode 100644
index 0000000..6349d69
--- /dev/null
+++ b/pulp_ansible/app/tasks/upload.py
@@ -0,0 +1,69 @@
+import os
+import re
+import tarfile
+import uuid
+
+from pulpcore.plugin.models import Artifact, ProgressBar, Repository, RepositoryVersion
+
+from pulp_ansible.app.models import AnsibleRole, AnsibleRoleVersion
+
+
+def import_content_from_tarball(namespace, artifact_pk=None, repository_pk=None):
+ """
+ Import Ansible content from a tarball saved as an Artifact.
+
+ The artifact is only a temporary storage area, and is deleted after being analyzed for more
+ content. Currently this task correctly handles: AnsibleRole and AnsibleRoleVersion content.
+
+ Args:
+ namespace (str): The namespace for any Ansible content to create
+ artifact_pk (int): The pk of the tarball Artifact to analyze and then delete
+ repository_pk (int): The repository that all created content should be associated with.
+ """
+ repository = Repository.objects.get(pk=repository_pk)
+ artifact = Artifact.objects.get(pk=artifact_pk)
+ role_paths = set()
+ with tarfile.open(str(artifact.file), "r") as tar:
+ artifact.delete() # this artifact is only stored between the frontend and backend
+ for tarinfo in tar:
+ match = re.search('(.*)/(tasks|handlers|defaults|vars|files|templates|meta)/main.yml',
+ tarinfo.path)
+ if match:
+ # This is a role asset
+ role_path = match.group(1)
+ role_paths.add(role_path)
+
+ tar.extractall()
+
+ role_version_pks = []
+ with ProgressBar(message='Importing Roles', total=len(role_paths)) as pb:
+ for role_path in role_paths:
+ match = re.search('(.*/)(.*)$', role_path)
+ role_name = match.group(2)
+ for tarinfo in tar:
+ if tarinfo.path == role_path:
+ # This is the role itself
+ assert tarinfo.isdir()
+ tarball_name = "{name}.tar.gz".format(name=role_name)
+ with tarfile.open(tarball_name, "w:gz") as newtar:
+ current_dir = os.getcwd()
+ os.chdir(match.group(1))
+ newtar.add(role_name)
+ os.chdir(current_dir)
+ full_path = os.path.abspath(tarball_name)
+ new_artifact = Artifact.init_and_validate(full_path)
+ new_artifact.save()
+ role, created = AnsibleRole.objects.get_or_create(namespace=namespace,
+ name=role_name)
+ version = uuid.uuid4()
+ role_version = AnsibleRoleVersion(
+ role=role,
+ version=version
+ )
+ role_version.artifact = new_artifact
+ role_version.save()
+ role_version_pks.append(role_version.pk)
+ pb.increment()
+ with RepositoryVersion.create(repository) as new_version:
+ qs = AnsibleRoleVersion.objects.filter(pk__in=role_version_pks)
+ new_version.add_content(qs)
diff --git a/pulp_ansible/app/urls.py b/pulp_ansible/app/urls.py
index 4b7f872..efabbb3 100644
--- a/pulp_ansible/app/urls.py
+++ b/pulp_ansible/app/urls.py
@@ -3,10 +3,13 @@ from django.conf.urls import url
from pulp_ansible.app.galaxy.views import (
AnsibleGalaxyVersionView,
AnsibleRoleList,
- AnsibleRoleVersionList
+ AnsibleRoleVersionList,
)
+from .viewsets import OneShotUploadView
+
urlpatterns = [
+ url(r'pulp_ansible/upload/$', OneShotUploadView.as_view()),
url(r'pulp_ansible/galaxy/(?P<path>.+)/api/$', AnsibleGalaxyVersionView.as_view()),
url(r'pulp_ansible/galaxy/(?P<path>.+)/api/v1/roles/$', AnsibleRoleList.as_view()),
url(r'pulp_ansible/galaxy/(?P<path>.+)/api/v1/roles/(?P<role_pk>[0-9a-f-]+)/versions/$',
diff --git a/pulp_ansible/app/viewsets.py b/pulp_ansible/app/viewsets.py
index 8106467..7b03209 100644
--- a/pulp_ansible/app/viewsets.py
+++ b/pulp_ansible/app/viewsets.py
@@ -1,7 +1,7 @@
from django.db import transaction
from drf_yasg.utils import swagger_auto_schema
from rest_framework.decorators import detail_route
-from rest_framework import mixins, status
+from rest_framework import mixins, status, views
from rest_framework.response import Response
from pulpcore.plugin.models import Artifact, RepositoryVersion, Publication
@@ -22,7 +22,7 @@ from pulpcore.plugin.viewsets import (
from . import tasks
from .models import AnsibleRemote, AnsibleRole, AnsibleRoleVersion
from .serializers import (AnsibleRemoteSerializer, AnsibleRoleSerializer,
- AnsibleRoleVersionSerializer)
+ AnsibleRoleVersionSerializer, OneShotUploadSerializer)
class AnsibleRoleFilter(BaseFilterSet):
@@ -188,3 +188,32 @@ class AnsiblePublicationsViewSet(NamedModelViewSet,
}
)
return OperationPostponedResponse(result, request)
+
+
+class OneShotUploadView(views.APIView):
+ """
+ ViewSet for One Shot Upload API.
+ """
+
+ @transaction.atomic
+ def post(self, request):
+ """Upload an Ansible Role."""
+ serializer = OneShotUploadSerializer(
+ data=request.data, context={'request': request})
+ serializer.is_valid(raise_exception=True)
+ data = serializer.validated_data
+ expected_digests = {'sha256': data['sha256']}
+
+ artifact = Artifact.init_and_validate(request.data['file'],
+ expected_digests=expected_digests)
+ artifact.save()
+
+ repository = data['repository']
+ async_result = enqueue_with_reservation(
+ tasks.import_content_from_tarball, [repository],
+ kwargs={
+ 'namespace': request.user.username,
+ 'artifact_pk': artifact.pk,
+ 'repository_pk': repository.pk
+ })
+ return OperationPostponedResponse(async_result, request)
from pulp_ansible.
From: @RCMariko (rchan)
Date: 2019-07-22T11:27:54Z
Since this issue is blocked by a new feature/change in Galaxy, can we add a tracker.discussion in the Ansible Galaxy project that can be added to this issue indicating that relationship?
from pulp_ansible.
From: @bmbouter (bmbouter)
Date: 2019-07-22T12:09:21Z
My perspective on what is blocking this issue isn't a Galaxy change, it's that a user or stakeholder hasn't prioritized it because it's "role" content (where the focus currently is "collection" content). If it did have a champion, we could move forward without any code changes in external projects by advising pulp users to add a 'version' to their role metadata.
I want to share some background on the placeholders idea. I agree we don't really have a good way to indicate what this ticket is waiting on and a tracker would do that. We used to have the 'External' Redmine project to have placeholders like that. We ran into a challenge with those placeholders where they got unblocked but never updated in Redmine so we mistakenly thought unblocked work was blocked. At some point on pulp-dev to delete the External Redmine project and instead link to external to Redmine where the work is happening (fedora bugs, upstream bugs, etc). Thoughts or suggestions on how to make this better are welcome.
from pulp_ansible.
Related Issues (20)
- AttributeError: 'NoneType' object has no attribute 'get'
- collection download counters prevent using read-only database replicas
- x-repo search incompatible with namespacemetadata architecture
- Performance issue on `pulp/api/v3/repositories/ansible/ansible/`
- param `order_by=version` doesn't sort correctly
- Sync should not fail if namespace's avatar fails to download HOT 1
- Collection sync update_highest_version calculation can potentially fail on re-sync
- Add Domain (multi-tenancy) support
- `is_latest` violates content immutability HOT 1
- Katello: Pulp-To-Pulp sync ends in 403 error
- Adjust access policies for syncronous label api
- Race condition on multiple nearly-simultaneous uploads of the same collection (different versions) HOT 1
- traceback on sync with new namespace "user" field
- Parsing CollectionVersion Metadata sync progress report does not report accurate total when using signed_only and remote has no signatures.
- Add `count` attribute to `TagSerializer` HOT 1
- `is_highest` update query can still fail since Django doesn't order the query for Postgres
- Syncing from https://galaxy.ansible.com/ failed with "get() returned more than one Collection -- it returned 2!"
- Duplicate uploads should complete using Pulp retrieve logic.
- Add missing distribution validation for `move_collection_version` and `copy_collection_version` endpoints
- git_url (and others) are bad fields on collection content HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from pulp_ansible.