From f114b1ce17c1d95c98c12c97b904c7c5b706be2b Mon Sep 17 00:00:00 2001 From: Gergely Brautigam <182850+Skarlso@users.noreply.github.com> Date: Mon, 8 Aug 2022 15:28:07 +0200 Subject: [PATCH] Github Access method (#36) * Github Access method * Added tests * Added testing for Credential passing * AM github: align with actual usage and support enterprise Co-authored-by: Uwe Krueger --- .gitignore | 3 +- docs/formats/accessmethods/README.md | 20 +- docs/reference/ocm_credentials_get.md | 1 + docs/reference/ocm_get_credentials.md | 1 + go.mod | 6 +- go.sum | 23 +- pkg/contexts/credentials/const.go | 1 + pkg/contexts/credentials/core/const.go | 1 + .../credentials/identity/hostpath/id_test.go | 166 +++++++++ .../credentials/identity/hostpath/identity.go | 94 +++++ .../identity/hostpath/suite_test.go | 27 ++ pkg/contexts/oci/attrs/cacheattr/attr.go | 1 + pkg/contexts/oci/identity/identity.go | 63 +--- .../ocm/accessmethods/github/README.md | 45 +++ .../ocm/accessmethods/github/downloader.go | 35 ++ .../ocm/accessmethods/github/method.go | 324 ++++++++++++++++++ .../ocm/accessmethods/github/method_test.go | 310 +++++++++++++++++ .../ocm/accessmethods/github/suite_test.go | 27 ++ .../accessmethods/github/testdata/repo.tar.gz | Bin 0 -> 272 bytes pkg/contexts/ocm/accessmethods/init.go | 1 + .../v3alpha1/jsonscheme/bindata.go | 2 +- .../versions/v2/jsonscheme/bindata.go | 2 +- pkg/mime/types.go | 1 + 23 files changed, 1074 insertions(+), 80 deletions(-) create mode 100644 pkg/contexts/credentials/identity/hostpath/id_test.go create mode 100644 pkg/contexts/credentials/identity/hostpath/identity.go create mode 100644 pkg/contexts/credentials/identity/hostpath/suite_test.go create mode 100644 pkg/contexts/ocm/accessmethods/github/README.md create mode 100644 pkg/contexts/ocm/accessmethods/github/downloader.go create mode 100644 pkg/contexts/ocm/accessmethods/github/method.go create mode 100644 pkg/contexts/ocm/accessmethods/github/method_test.go create mode 100644 pkg/contexts/ocm/accessmethods/github/suite_test.go create mode 100644 pkg/contexts/ocm/accessmethods/github/testdata/repo.tar.gz diff --git a/.gitignore b/.gitignore index 9c044ac1b..146119fc3 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,8 @@ main /dist /tmp -/local +local/ /ocm .vscode +/gen /cmds/test/generics diff --git a/docs/formats/accessmethods/README.md b/docs/formats/accessmethods/README.md index 9c978857b..ce7ebc583 100644 --- a/docs/formats/accessmethods/README.md +++ b/docs/formats/accessmethods/README.md @@ -65,14 +65,14 @@ The following method types are centrally defined and available in the OCM toolse Access of an OCI artefact stored in any OCI registry. -- [`ociRegistry`](../../../pkg/contexts/ocm/accessmethods/ociartefact/README.md) *external* (deprecated) - - It is a legacy name on the new official access method `ociArtefact` - - [`ociBlob`](../../../pkg/contexts/ocm/accessmethods/ociblob/README.md) *external* Access of an OCI artefact stored in any OCI registry. +- [`gitHub`](../../../pkg/contexts/ocm/accessmethods/github/README.md) *external* + + Access of a git commit in a [github](https://github.com) repository. + - [`localBlob`](../../../pkg/contexts/ocm/accessmethods/localblob/README.md) *local* This is a special access method that has no global implementation. @@ -81,4 +81,14 @@ The following method types are centrally defined and available in the OCM toolse MUST explicitly provide an implementation for this method. For example, in an OCI based OCM repository the implementation stores - local blobs as additional artefact layers according to the OCI model. \ No newline at end of file + local blobs as additional artefact layers according to the OCI model. + +### Legacy Types + +- [`ociRegistry`](../../../pkg/contexts/ocm/accessmethods/ociartefact/README.md) *external* (deprecated) + + It is a legacy name on the new official access method `ociArtefact` + +- [`github`](../../../pkg/contexts/ocm/accessmethods/github/README.md) *external* + + It is a legacy name on the new official access method `gitHub` \ No newline at end of file diff --git a/docs/reference/ocm_credentials_get.md b/docs/reference/ocm_credentials_get.md index f42d0c5d3..b9cb127a9 100644 --- a/docs/reference/ocm_credentials_get.md +++ b/docs/reference/ocm_credentials_get.md @@ -23,6 +23,7 @@ For the following usage contexts with matchers and standard identity matchers ex - OCIRegistry: OCI registry credential matcher - exact: exact match of given pattern set + - hostpath: Host and path based credential matcher - partial: complete match of given pattern ignoring additional attributes The used matcher is derived from the consumer attribute type. diff --git a/docs/reference/ocm_get_credentials.md b/docs/reference/ocm_get_credentials.md index 40eaeca0c..03cafc12d 100644 --- a/docs/reference/ocm_get_credentials.md +++ b/docs/reference/ocm_get_credentials.md @@ -23,6 +23,7 @@ For the following usage contexts with matchers and standard identity matchers ex - OCIRegistry: OCI registry credential matcher - exact: exact match of given pattern set + - hostpath: Host and path based credential matcher - partial: complete match of given pattern ignoring additional attributes The used matcher is derived from the consumer attribute type. diff --git a/go.mod b/go.mod index c583a839e..3f628f357 100644 --- a/go.mod +++ b/go.mod @@ -34,6 +34,7 @@ require ( github.com/docker/go-connections v0.4.0 github.com/drone/envsubst v1.0.3 github.com/golang/mock v1.6.0 + github.com/google/go-github/v45 v45.2.0 github.com/klauspost/compress v1.14.4 github.com/klauspost/pgzip v1.2.5 github.com/mandelsoft/vfs v0.0.0-20220401225935-42c25028b498 @@ -43,6 +44,7 @@ require ( github.com/stretchr/testify v1.7.0 github.com/ulikunitz/xz v0.5.10 golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 + golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f golang.org/x/text v0.3.7 gopkg.in/yaml.v3 v3.0.1 helm.sh/helm/v3 v3.8.2 @@ -65,7 +67,8 @@ require ( github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.2 // indirect - github.com/google/go-cmp v0.5.6 // indirect + github.com/google/go-cmp v0.5.8 // indirect + github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect @@ -92,6 +95,7 @@ require ( golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368 // indirect google.golang.org/grpc v1.43.0 // indirect google.golang.org/protobuf v1.27.1 // indirect diff --git a/go.sum b/go.sum index 234139f16..ddb21176e 100644 --- a/go.sum +++ b/go.sum @@ -71,7 +71,6 @@ github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= @@ -102,7 +101,6 @@ github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwT github.com/Microsoft/hcsshim v0.8.23/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01nnU2M8jKDg= github.com/Microsoft/hcsshim v0.9.2/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc= github.com/Microsoft/hcsshim v0.9.3 h1:k371PzBuRrz2b+ebGuI2nVgVhgsVX60jMfSw80NECxo= -github.com/Microsoft/hcsshim v0.9.3/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc= github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= @@ -170,7 +168,6 @@ github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInq github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= @@ -263,7 +260,6 @@ github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZH github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk= github.com/containerd/go-cni v1.1.0/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA= github.com/containerd/go-cni v1.1.3/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA= -github.com/containerd/go-cni v1.1.6/go.mod h1:BWtoWl5ghVymxu6MBjg79W9NZrCRyHIdUtk4cauMe34= github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g= @@ -274,7 +270,6 @@ github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6T github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow= github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms= github.com/containerd/imgcrypt v1.1.3/go.mod h1:/TPA1GIDXMzbj01yd8pIbQiLdQxed5ue1wb8bP7PQu4= -github.com/containerd/imgcrypt v1.1.4/go.mod h1:LorQnPtzL/T0IyCeftcsMEO7AqxUDbdO8j/tSUpgxvo= github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= @@ -299,11 +294,9 @@ github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= github.com/containernetworking/cni v1.0.1/go.mod h1:AKuhXbN5EzmD4yTNtfSsX3tPcmtrBI6QcRV0NiNt15Y= -github.com/containernetworking/cni v1.1.1/go.mod h1:sDpYKmGVENF3s6uvMvGgldDWeG8dMxakj/u+i9ht9vw= github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM= github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= github.com/containernetworking/plugins v1.0.1/go.mod h1:QHCfGpaTwYTbbH+nZXKVTxNBDZcxSOplJT5ico8/FLE= -github.com/containernetworking/plugins v1.1.1/go.mod h1:Sr5TH/eBsGLXK/h71HeLfX19sZPp3ry5uHSkI4LPxV8= github.com/containers/image/v5 v5.20.0 h1:BYFMRvYqmEHnHo0sjTbnLbj0fzkGLDx6P57lszm30B4= github.com/containers/image/v5 v5.20.0/go.mod h1:5UL1ooih6+USVYXk19r8ScQNsbTprhlJxrHezAu4OVE= github.com/containers/libtrust v0.0.0-20200511145503-9c3a6c22cd9a h1:spAGlqziZjCJL25C6F1zsQY05tfCKE9F5YwtEWWe6hU= @@ -556,10 +549,15 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0= +github.com/google/go-github/v45 v45.2.0 h1:5oRLszbrkvxDDqBCNj2hjDZMKmvexaZ1xw/FCD+K3FI= +github.com/google/go-github/v45 v45.2.0/go.mod h1:FObaZJEDSTa/WGCzZ2Z3eoCDXWJKMenWWTrd8jrta28= github.com/google/go-intervals v0.0.2/go.mod h1:MkaR3LNRfeKLPmqgJYs4E66z5InYjmCjbbr4TQlcT6Y= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= @@ -578,7 +576,6 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -804,7 +801,6 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= -github.com/networkplumbing/go-nft v0.2.0/go.mod h1:HnnM+tYvlGAsMU7yoYwXEVLLiDW9gdMmb5HoGcwpuQs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= @@ -828,7 +824,6 @@ github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9k github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -851,7 +846,6 @@ github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zM github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.0.2-0.20211117181255-693428a734f5/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.0.3-0.20211202193544-a5463b7f9c84 h1:g47eG1u/gw0JB7mZ88TcHKCmsy7sWUNZD8ZS9Jhi0O8= github.com/opencontainers/image-spec v1.0.3-0.20211202193544-a5463b7f9c84/go.mod h1:Qnt1q4cjDNQI9bT832ziho5Iw2BhK8o1KwLOwW56VP4= github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= @@ -861,7 +855,6 @@ github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rm github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= github.com/opencontainers/runc v1.1.0/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc= -github.com/opencontainers/runc v1.1.2/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc= github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= @@ -873,7 +866,6 @@ github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqi github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= -github.com/opencontainers/selinux v1.10.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/ostreedev/ostree-go v0.0.0-20210805093236-719684c64e4f/go.mod h1:J6OG6YJVEWopen4avK3VNQSnALmmjvniMmni/YFYAwc= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= @@ -1258,6 +1250,7 @@ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f h1:Qmd2pbz05z7z6lm0DrgQVVPuBm92jqujBKMHMOlOQEw= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1503,6 +1496,7 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -1642,7 +1636,6 @@ gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= diff --git a/pkg/contexts/credentials/const.go b/pkg/contexts/credentials/const.go index 9b4d9321b..f6b63a6c3 100644 --- a/pkg/contexts/credentials/const.go +++ b/pkg/contexts/credentials/const.go @@ -26,4 +26,5 @@ const ( ATTR_SERVER_ADDRESS = core.ATTR_SERVER_ADDRESS ATTR_IDENTITY_TOKEN = core.ATTR_IDENTITY_TOKEN ATTR_REGISTRY_TOKEN = core.ATTR_REGISTRY_TOKEN + ATTR_TOKEN = core.ATTR_TOKEN ) diff --git a/pkg/contexts/credentials/core/const.go b/pkg/contexts/credentials/core/const.go index 794b7d834..c3fdc9d71 100644 --- a/pkg/contexts/credentials/core/const.go +++ b/pkg/contexts/credentials/core/const.go @@ -22,4 +22,5 @@ const ( ATTR_SERVER_ADDRESS = "serverAddress" ATTR_IDENTITY_TOKEN = "identityToken" ATTR_REGISTRY_TOKEN = "registryToken" + ATTR_TOKEN = "token" ) diff --git a/pkg/contexts/credentials/identity/hostpath/id_test.go b/pkg/contexts/credentials/identity/hostpath/id_test.go new file mode 100644 index 000000000..9945106f5 --- /dev/null +++ b/pkg/contexts/credentials/identity/hostpath/id_test.go @@ -0,0 +1,166 @@ +// Copyright 2022 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hostpath_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "github.com/open-component-model/ocm/pkg/contexts/credentials" + "github.com/open-component-model/ocm/pkg/contexts/credentials/core" + "github.com/open-component-model/ocm/pkg/contexts/credentials/identity/hostpath" +) + +func IdentityMatcher(pattern, cur, id core.ConsumerIdentity) bool { + return hostpath.IdentityMatcher("OCIRegistry")(pattern, cur, id) +} + +var _ = Describe("ctf management", func() { + + Context("with path", func() { + pat := credentials.ConsumerIdentity{ + hostpath.ID_HOSTNAME: "host", + hostpath.ID_PATHPREFIX: "a/b", + hostpath.ID_PORT: "4711", + } + + It("complete", func() { + id := credentials.ConsumerIdentity{ + hostpath.ID_HOSTNAME: "host", + hostpath.ID_PATHPREFIX: "a/b", + hostpath.ID_PORT: "4711", + } + Expect(IdentityMatcher(pat, nil, id)).To(BeTrue()) + Expect(IdentityMatcher(pat, id, id)).To(BeFalse()) + }) + + It("path prefix", func() { + id := credentials.ConsumerIdentity{ + hostpath.ID_HOSTNAME: "host", + hostpath.ID_PATHPREFIX: "a", + hostpath.ID_PORT: "4711", + } + Expect(IdentityMatcher(pat, nil, id)).To(BeTrue()) + Expect(IdentityMatcher(pat, pat, id)).To(BeFalse()) + }) + It("different prefix", func() { + id := credentials.ConsumerIdentity{ + hostpath.ID_HOSTNAME: "host", + hostpath.ID_PATHPREFIX: "b", + hostpath.ID_PORT: "4711", + } + Expect(IdentityMatcher(pat, nil, id)).To(BeFalse()) + Expect(IdentityMatcher(pat, pat, id)).To(BeFalse()) + }) + It("longer prefix", func() { + id := credentials.ConsumerIdentity{ + hostpath.ID_HOSTNAME: "host", + hostpath.ID_PATHPREFIX: "a/b/c", + hostpath.ID_PORT: "4711", + } + Expect(IdentityMatcher(pat, nil, id)).To(BeFalse()) + Expect(IdentityMatcher(pat, pat, id)).To(BeFalse()) + }) + It("missing path", func() { + id := credentials.ConsumerIdentity{ + hostpath.ID_HOSTNAME: "host", + hostpath.ID_PORT: "4711", + } + Expect(IdentityMatcher(pat, nil, id)).To(BeTrue()) + Expect(IdentityMatcher(pat, pat, id)).To(BeFalse()) + }) + It("missing port", func() { + id := credentials.ConsumerIdentity{ + hostpath.ID_HOSTNAME: "host", + hostpath.ID_PATHPREFIX: "a/b", + } + Expect(IdentityMatcher(pat, nil, id)).To(BeTrue()) + Expect(IdentityMatcher(pat, pat, id)).To(BeFalse()) + + Expect(IdentityMatcher(id, nil, pat)).To(BeTrue()) // accept additional port as fallback + Expect(IdentityMatcher(id, id, pat)).To(BeFalse()) // but not to replace more general match + }) + It("different port", func() { + id := credentials.ConsumerIdentity{ + hostpath.ID_HOSTNAME: "host", + hostpath.ID_PATHPREFIX: "a/b", + hostpath.ID_PORT: "0815", + } + Expect(IdentityMatcher(pat, nil, id)).To(BeFalse()) + Expect(IdentityMatcher(pat, pat, id)).To(BeFalse()) + }) + + It("different host", func() { + id := credentials.ConsumerIdentity{ + hostpath.ID_HOSTNAME: "other", + hostpath.ID_PATHPREFIX: "a/b", + hostpath.ID_PORT: "4711", + } + Expect(IdentityMatcher(pat, nil, id)).To(BeFalse()) + Expect(IdentityMatcher(pat, pat, id)).To(BeFalse()) + }) + It("no host", func() { + id := credentials.ConsumerIdentity{ + hostpath.ID_PATHPREFIX: "a/b", + hostpath.ID_PORT: "4711", + } + Expect(IdentityMatcher(id, nil, pat)).To(BeTrue()) + Expect(IdentityMatcher(pat, id, id)).To(BeFalse()) + Expect(IdentityMatcher(pat, id, pat)).To(BeTrue()) + }) + }) + + Context("without path", func() { + pat := credentials.ConsumerIdentity{ + hostpath.ID_HOSTNAME: "host", + hostpath.ID_PORT: "4711", + } + + It("complete", func() { + id := credentials.ConsumerIdentity{ + hostpath.ID_HOSTNAME: "host", + hostpath.ID_PORT: "4711", + } + Expect(IdentityMatcher(pat, nil, id)).To(BeTrue()) + Expect(IdentityMatcher(pat, id, id)).To(BeFalse()) + }) + + It("different prefix", func() { + id := credentials.ConsumerIdentity{ + hostpath.ID_HOSTNAME: "host", + hostpath.ID_PORT: "4711", + hostpath.ID_PATHPREFIX: "b", + } + Expect(IdentityMatcher(pat, nil, id)).To(BeFalse()) + Expect(IdentityMatcher(pat, pat, id)).To(BeFalse()) + }) + It("missing port", func() { + id := credentials.ConsumerIdentity{ + hostpath.ID_HOSTNAME: "host", + } + Expect(IdentityMatcher(pat, nil, id)).To(BeTrue()) + Expect(IdentityMatcher(pat, pat, id)).To(BeFalse()) + }) + It("different port", func() { + id := credentials.ConsumerIdentity{ + hostpath.ID_HOSTNAME: "host", + hostpath.ID_PORT: "0815", + } + Expect(IdentityMatcher(pat, nil, id)).To(BeFalse()) + Expect(IdentityMatcher(pat, pat, id)).To(BeFalse()) + }) + }) + +}) diff --git a/pkg/contexts/credentials/identity/hostpath/identity.go b/pkg/contexts/credentials/identity/hostpath/identity.go new file mode 100644 index 000000000..9185e356c --- /dev/null +++ b/pkg/contexts/credentials/identity/hostpath/identity.go @@ -0,0 +1,94 @@ +// Copyright 2022 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hostpath + +import ( + "strings" + + "github.com/open-component-model/ocm/pkg/contexts/credentials/core" + "github.com/open-component-model/ocm/pkg/contexts/credentials/cpi" +) + +// IDENTITY_TYPE is the identity of this matcher. +const IDENTITY_TYPE = "hostpath" + +// ID_HOSTNAME is a hostname. +const ID_HOSTNAME = "hostname" + +// ID_PORT is a port. +const ID_PORT = "port" + +// ID_PATHPREFIX is the path prefix below the host. +const ID_PATHPREFIX = "pathprefix" + +func init() { + cpi.RegisterIdentityMatcher(IDENTITY_TYPE, IdentityMatcher(""), "Host and path based credential matcher") +} + +func IdentityMatcher(identityType string) cpi.IdentityMatcher { + return func(pattern, cur, id core.ConsumerIdentity) bool { + if identityType != "" && pattern[identityType] != "" && id[identityType] != "" && pattern[identityType] != id[identityType] { + return false + } + if pattern[ID_HOSTNAME] != "" && pattern[ID_HOSTNAME] != id[ID_HOSTNAME] { + return false + } + + if pattern[ID_PORT] != "" { + if id[ID_PORT] != "" && id[ID_PORT] != pattern[ID_PORT] { + return false + } + } + + if pattern[ID_PATHPREFIX] != "" { + if id[ID_PATHPREFIX] != "" { + if len(id[ID_PATHPREFIX]) > len(pattern[ID_PATHPREFIX]) { + return false + } + pcomps := strings.Split(pattern[ID_PATHPREFIX], "/") + icomps := strings.Split(id[ID_PATHPREFIX], "/") + if len(icomps) > len(pcomps) { + return false + } + for i := range icomps { + if pcomps[i] != icomps[i] { + return false + } + } + } + } else { + if id[ID_PATHPREFIX] != "" { + return false + } + } + + // ok now it basically matches, check against current match + if len(cur) == 0 { + return true + } + + if cur[ID_HOSTNAME] == "" && id[ID_HOSTNAME] != "" { + return true + } + if cur[ID_PORT] == "" && (id[ID_PORT] != "" && pattern[ID_PORT] != "") { + return true + } + + if len(cur[ID_PATHPREFIX]) < len(id[ID_PATHPREFIX]) { + return true + } + return false + } +} diff --git a/pkg/contexts/credentials/identity/hostpath/suite_test.go b/pkg/contexts/credentials/identity/hostpath/suite_test.go new file mode 100644 index 000000000..aa411718e --- /dev/null +++ b/pkg/contexts/credentials/identity/hostpath/suite_test.go @@ -0,0 +1,27 @@ +// Copyright 2022 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hostpath_test + +import ( + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +func TestConfig(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Hostpath Identity Test Suite") +} diff --git a/pkg/contexts/oci/attrs/cacheattr/attr.go b/pkg/contexts/oci/attrs/cacheattr/attr.go index 29cf55502..cb2ea75d5 100644 --- a/pkg/contexts/oci/attrs/cacheattr/attr.go +++ b/pkg/contexts/oci/attrs/cacheattr/attr.go @@ -64,6 +64,7 @@ func (a AttributeType) Decode(data []byte, unmarshaller runtime.Unmarshaler) (in } value = home + value[1:] } + // TODO: This should use the virtual filesystem. err = os.MkdirAll(value, 0700) if err == nil { return accessio.NewStaticBlobCache(value) diff --git a/pkg/contexts/oci/identity/identity.go b/pkg/contexts/oci/identity/identity.go index 47a9f5bf4..028895a3c 100644 --- a/pkg/contexts/oci/identity/identity.go +++ b/pkg/contexts/oci/identity/identity.go @@ -15,77 +15,28 @@ package identity import ( - "strings" - "github.com/open-component-model/ocm/pkg/contexts/credentials/cpi" + "github.com/open-component-model/ocm/pkg/contexts/credentials/identity/hostpath" ) // CONSUMER_TYPE is the OCT registry type const CONSUMER_TYPE = "OCIRegistry" // ID_HOSTNAME is the hostname of an OCT repository -const ID_HOSTNAME = "hostname" +const ID_HOSTNAME = hostpath.ID_HOSTNAME // ID_PORT is the port number of an OCT repository -const ID_PORT = "port" +const ID_PORT = hostpath.ID_PORT // ID_PATHPREFIX is the artefact prefix -const ID_PATHPREFIX = "pathprefix" +const ID_PATHPREFIX = hostpath.ID_PATHPREFIX func init() { cpi.RegisterIdentityMatcher(CONSUMER_TYPE, IdentityMatcher, "OCI registry credential matcher") } -func IdentityMatcher(pattern, cur, id cpi.ConsumerIdentity) bool { - if pattern[cpi.CONSUMER_ATTR_TYPE] != "" && id[cpi.CONSUMER_ATTR_TYPE] != "" && pattern[cpi.CONSUMER_ATTR_TYPE] != id[cpi.CONSUMER_ATTR_TYPE] { - return false - } - if pattern[ID_HOSTNAME] != "" && pattern[ID_HOSTNAME] != id[ID_HOSTNAME] { - return false - } - - if pattern[ID_PORT] != "" { - if id[ID_PORT] != "" && id[ID_PORT] != pattern[ID_PORT] { - return false - } - } - - if pattern[ID_PATHPREFIX] != "" { - if id[ID_PATHPREFIX] != "" { - if len(id[ID_PATHPREFIX]) > len(pattern[ID_PATHPREFIX]) { - return false - } - pcomps := strings.Split(pattern[ID_PATHPREFIX], "/") - icomps := strings.Split(id[ID_PATHPREFIX], "/") - if len(icomps) > len(pcomps) { - return false - } - for i := range icomps { - if pcomps[i] != icomps[i] { - return false - } - } - } - } else { - if id[ID_PATHPREFIX] != "" { - return false - } - } +var identityMatcher = hostpath.IdentityMatcher(CONSUMER_TYPE) - // ok now it basically matches, check against current match - if len(cur) == 0 { - return true - } - - if cur[ID_HOSTNAME] == "" && id[ID_HOSTNAME] != "" { - return true - } - if cur[ID_PORT] == "" && (id[ID_PORT] != "" && pattern[ID_PORT] != "") { - return true - } - - if len(cur[ID_PATHPREFIX]) < len(id[ID_PATHPREFIX]) { - return true - } - return false +func IdentityMatcher(pattern, cur, id cpi.ConsumerIdentity) bool { + return identityMatcher(pattern, cur, id) } diff --git a/pkg/contexts/ocm/accessmethods/github/README.md b/pkg/contexts/ocm/accessmethods/github/README.md new file mode 100644 index 000000000..6ee209fae --- /dev/null +++ b/pkg/contexts/ocm/accessmethods/github/README.md @@ -0,0 +1,45 @@ + +# Access Method `gitHub` and `github` - Github Commit Access + + +### Synopsis + +``` +type: gitHub/v1 +``` + +Provided blobs use the following media type for: `application/x-tgz` + +The artefact content is provided as gnu-zipped tar archive + +### Description + +This method implements the access of the content of a git commit stored in a +GitHub repository. + +Supported specification version is `v1` + + + +### Specification Versions + +#### Version `v1` + +The type specific specification fields are: + +- **`repoUrl`** *string* + + Repository URL with or without scheme. + +- **`ref`** (optional) *string* + + Original ref used to get the commit from + +- **`commit`** *string* + + The sha/id of the git commit + + +### Go Bindings + +The go binding can be found [here](method.go) diff --git a/pkg/contexts/ocm/accessmethods/github/downloader.go b/pkg/contexts/ocm/accessmethods/github/downloader.go new file mode 100644 index 000000000..8cb7a0406 --- /dev/null +++ b/pkg/contexts/ocm/accessmethods/github/downloader.go @@ -0,0 +1,35 @@ +package github + +import ( + "bytes" + "fmt" + "io" + "net/http" +) + +// Downloader defines an abstraction for downloading an archive from GitHub. +type Downloader interface { + Download(link string) ([]byte, error) +} + +// HTTPDownloader simply uses the default HTTP client to download the contents of a URL. +type HTTPDownloader struct{} + +func (h *HTTPDownloader) Download(link string) ([]byte, error) { + httpResp, err := http.Get(link) + if err != nil { + return nil, err + } + defer func() { + if err := httpResp.Body.Close(); err != nil { + fmt.Println("failed to close body: ", err) + } + }() + + var blob []byte + buf := bytes.NewBuffer(blob) + if _, err := io.Copy(buf, httpResp.Body); err != nil { + return nil, err + } + return buf.Bytes(), nil +} diff --git a/pkg/contexts/ocm/accessmethods/github/method.go b/pkg/contexts/ocm/accessmethods/github/method.go new file mode 100644 index 000000000..1b689028a --- /dev/null +++ b/pkg/contexts/ocm/accessmethods/github/method.go @@ -0,0 +1,324 @@ +// Copyright 2022 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package github + +import ( + "context" + "fmt" + "io" + "net/http" + "net/url" + "strings" + "sync" + "unicode" + + "github.com/google/go-github/v45/github" + "golang.org/x/oauth2" + + "github.com/open-component-model/ocm/pkg/common/accessio" + "github.com/open-component-model/ocm/pkg/contexts/credentials" + "github.com/open-component-model/ocm/pkg/contexts/credentials/identity/hostpath" + "github.com/open-component-model/ocm/pkg/contexts/oci/identity" + "github.com/open-component-model/ocm/pkg/contexts/oci/repositories/artefactset" + "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" + "github.com/open-component-model/ocm/pkg/errors" + "github.com/open-component-model/ocm/pkg/mime" + "github.com/open-component-model/ocm/pkg/runtime" +) + +// Type is the access type of GitHub registry. +const Type = "gitHub" +const TypeV1 = Type + runtime.VersionSeparator + "v1" + +const LegacyType = "github" +const LegacyTypeV1 = LegacyType + runtime.VersionSeparator + "v1" + +const CONSUMER_TYPE = "Github" + +const ShaLength = 40 + +func init() { + cpi.RegisterAccessType(cpi.NewAccessSpecType(Type, &AccessSpec{})) + cpi.RegisterAccessType(cpi.NewAccessSpecType(TypeV1, &AccessSpec{})) + cpi.RegisterAccessType(cpi.NewAccessSpecType(LegacyType, &AccessSpec{})) + cpi.RegisterAccessType(cpi.NewAccessSpecType(LegacyTypeV1, &AccessSpec{})) +} + +// AccessSpec describes the access for a GitHub registry. +type AccessSpec struct { + runtime.ObjectVersionedType `json:",inline"` + + // RepoUrl is the repository URL, with host, owner and repository + RepoURL string `json:"repoUrl"` + + // APIHostname is an optional different hostname for accessing the github REST API + // for enterprise installations + APIHostname string `json:"apiHostname,omitempty"` + + // Ref + Ref string `json:"ref,omitempty"` + // Commit defines the hash of the commit. + Commit string `json:"commit"` + + client *http.Client + downloader Downloader +} + +var _ cpi.AccessSpec = (*AccessSpec)(nil) + +// AccessSpecOptions defines a set of options which can be applied to the access spec. +type AccessSpecOptions func(s *AccessSpec) + +// WithRef creates an access spec with a specified reference field +func WithRef(ref string) AccessSpecOptions { + return func(s *AccessSpec) { + s.Ref = ref + } +} + +// WithClient creates an access spec with a custom http client. +func WithClient(client *http.Client) AccessSpecOptions { + return func(s *AccessSpec) { + s.client = client + } +} + +// WithDownloader defines a client with a custom downloader. +func WithDownloader(downloader Downloader) AccessSpecOptions { + return func(s *AccessSpec) { + s.downloader = downloader + } +} + +// New creates a new GitHub registry access spec version v1 +func New(hostname string, port int, repo, owner, commit string, opts ...AccessSpecOptions) *AccessSpec { + if hostname == "" { + hostname = "github.com" + } + p := "" + if port != 0 { + p = fmt.Sprintf(":%d", port) + } + url := fmt.Sprintf("%s%s/%s/%s", hostname, p, owner, repo) + s := &AccessSpec{ + ObjectVersionedType: runtime.NewVersionedObjectType(Type), + RepoURL: url, + Commit: commit, + } + for _, o := range opts { + o(s) + } + return s +} + +func (_ *AccessSpec) IsLocal(cpi.Context) bool { + return false +} + +func (_ *AccessSpec) GetType() string { + return Type +} + +func (a *AccessSpec) AccessMethod(c cpi.ComponentVersionAccess) (cpi.AccessMethod, error) { + return newMethod(c, a) +} + +//////////////////////////////////////////////////////////////////////////////// + +// RepositoryService defines capabilities of a GitHub repository. +type RepositoryService interface { + GetArchiveLink(ctx context.Context, owner, repo string, archiveformat github.ArchiveFormat, opts *github.RepositoryContentGetOptions, followRedirects bool) (*url.URL, *github.Response, error) +} + +type accessMethod struct { + lock sync.Mutex + blob artefactset.ArtefactBlob + compvers cpi.ComponentVersionAccess + spec *AccessSpec + repositoryService RepositoryService + owner string + repo string + downloader Downloader +} + +var _ cpi.AccessMethod = (*accessMethod)(nil) + +func newMethod(c cpi.ComponentVersionAccess, a *AccessSpec) (*accessMethod, error) { + unparsed := a.RepoURL + + if !strings.HasPrefix(unparsed, "https://") && !strings.HasPrefix(unparsed, "http://") { + unparsed = "https://" + unparsed + } + u, err := url.Parse(unparsed) + if err != nil { + return nil, errors.ErrInvalidWrap(err, "repository url", a.RepoURL) + } + + path := strings.Trim(u.Path, "/") + pathcomps := strings.Split(path, "/") + if len(pathcomps) != 2 { + return nil, errors.ErrInvalid("repository path", path, a.RepoURL) + } + + token, err := getCreds(u.Hostname(), u.Port(), path, c.GetContext().CredentialsContext()) + if err != nil { + return nil, fmt.Errorf("failed to get creds: %w", err) + } + + var client *github.Client + + httpclient := a.client + + if token != "" && httpclient == nil { + ts := oauth2.StaticTokenSource( + &oauth2.Token{AccessToken: token}, + ) + httpclient = oauth2.NewClient(context.Background(), ts) + } + if u.Hostname() == "github.com" { + client = github.NewClient(httpclient) + } else { + t := *u + t.Path = "" + if a.APIHostname != "" { + t.Host = a.APIHostname + } + + client, err = github.NewEnterpriseClient(t.String(), t.String(), httpclient) + if err != nil { + return nil, err + } + } + + var downloader Downloader = &HTTPDownloader{} + if a.downloader != nil { + downloader = a.downloader + } + return &accessMethod{ + spec: a, + compvers: c, + owner: pathcomps[0], + repo: pathcomps[1], + repositoryService: client.Repositories, + downloader: downloader, + }, nil +} + +func getCreds(hostname, port, path string, cctx credentials.Context) (string, error) { + id := credentials.ConsumerIdentity{ + credentials.CONSUMER_ATTR_TYPE: CONSUMER_TYPE, + identity.ID_HOSTNAME: hostname, + } + if port != "" { + id[identity.ID_PORT] = port + } + id[identity.ID_PATHPREFIX] = path + var creds credentials.Credentials + src, err := cctx.GetCredentialsForConsumer(id, hostpath.IdentityMatcher(CONSUMER_TYPE)) + if err != nil { + if !errors.IsErrUnknown(err) { + return "", err + } + return "", nil + } + if src != nil { + creds, err = src.Credentials(cctx) + if err != nil { + return "", err + } + } + return creds.GetProperty(credentials.ATTR_TOKEN), nil +} + +func (m *accessMethod) GetKind() string { + return Type +} + +// Close should clean up all cached data if present. +// Exp.: Cache the blob data. +func (m *accessMethod) Close() error { + m.lock.Lock() + defer m.lock.Unlock() + if m.blob != nil { + tmp := m.blob + m.blob = nil + return tmp.Close() + } + return nil +} + +func (m *accessMethod) Get() ([]byte, error) { + blob, err := m.getBlob() + if err != nil { + return nil, err + } + return blob.Get() +} + +func (m *accessMethod) Reader() (io.ReadCloser, error) { + b, err := m.getBlob() + if err != nil { + return nil, err + } + r, err := b.Reader() + if err != nil { + return nil, err + } + return r, nil +} + +func (m *accessMethod) MimeType() string { + return mime.MIME_TGZ +} + +// TODO: Implement caching based on the SHA of the blob. If it is detected that that SHA already exists +// return it. ( Use the virtual filesystem implementation so it can be in memory or via file system ). +func (m *accessMethod) getBlob() (accessio.BlobAccess, error) { + m.lock.Lock() + defer m.lock.Unlock() + if m.blob != nil { + return m.blob, nil + } + blob, err := m.downloadArchive() + if err != nil { + return nil, err + } + + return accessio.BlobAccessForData(mime.MIME_TGZ, blob), nil +} + +func (m *accessMethod) downloadArchive() ([]byte, error) { + if len(m.spec.Commit) != ShaLength { + return nil, fmt.Errorf("commit is not a SHA") + } + for _, c := range m.spec.Commit { + if !unicode.IsOneOf([]*unicode.RangeTable{unicode.Letter, unicode.Digit}, c) { + return nil, fmt.Errorf("commit contains invalid characters for a SHA") + } + } + + link, resp, err := m.repositoryService.GetArchiveLink(context.Background(), m.owner, m.repo, github.Tarball, &github.RepositoryContentGetOptions{ + Ref: m.spec.Commit, + }, true) + if err != nil { + return nil, err + } + defer func() { + if err := resp.Body.Close(); err != nil { + fmt.Println("failed to close body: ", err) + } + }() + return m.downloader.Download(link.String()) +} diff --git a/pkg/contexts/ocm/accessmethods/github/method_test.go b/pkg/contexts/ocm/accessmethods/github/method_test.go new file mode 100644 index 000000000..c420bbb44 --- /dev/null +++ b/pkg/contexts/ocm/accessmethods/github/method_test.go @@ -0,0 +1,310 @@ +// Copyright 2022 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package github_test + +import ( + "bytes" + "fmt" + "io/ioutil" + "net/http" + "os" + "path/filepath" + + _ "github.com/open-component-model/ocm/pkg/contexts/datacontext/config" + + "k8s.io/apimachinery/pkg/util/sets" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "github.com/open-component-model/ocm/pkg/common" + "github.com/open-component-model/ocm/pkg/contexts/credentials" + "github.com/open-component-model/ocm/pkg/contexts/credentials/core" + "github.com/open-component-model/ocm/pkg/contexts/ocm" + me "github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/github" + "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" +) + +const doPrivate = false + +type mockDownloader struct { + expected []byte + shouldMatchLink string +} + +// RoundTripFunc . +type RoundTripFunc func(req *http.Request) *http.Response + +// RoundTrip . +func (f RoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) { + return f(req), nil +} + +//NewTestClient returns *http.Client with Transport replaced to avoid making real calls +func NewTestClient(fn RoundTripFunc) *http.Client { + return &http.Client{ + Transport: fn, + } + +} + +func (m *mockDownloader) Download(link string) ([]byte, error) { + if link != m.shouldMatchLink { + return nil, fmt.Errorf("link mismatch; got: %s want: %s", link, m.shouldMatchLink) + } + + return m.expected, nil +} + +func Configure(ctx ocm.Context) { + data, err := ioutil.ReadFile(filepath.Join(os.Getenv("HOME"), ".ocmconfig")) + if err != nil { + return + } + _, err = ctx.ConfigContext().ApplyData(data, nil, ".ocmconfig") + ExpectWithOffset(1, err).ToNot(HaveOccurred()) + +} + +var _ = Describe("Method", func() { + var ( + ctx ocm.Context + expectedBlobContent []byte + err error + testClient *http.Client + defaultLink string + accessSpec *me.AccessSpec + ) + + BeforeEach(func() { + ctx = ocm.New() + expectedBlobContent, err = os.ReadFile(filepath.Join("testdata", "repo.tar.gz")) + Expect(err).ToNot(HaveOccurred()) + defaultLink = "https://github.com/test/test/sha?token=token" + + testClient = NewTestClient(func(req *http.Request) *http.Response { + return &http.Response{ + StatusCode: 302, + Status: http.StatusText(http.StatusFound), + Body: ioutil.NopCloser(bytes.NewBufferString(`{}`)), + // Must be set to non-nil value or it panics + Header: http.Header{ + "Location": []string{defaultLink}, + }, + } + }) + accessSpec = me.New( + "hostname", + 1234, + "repo", + "owner", + "7b1445755ee2527f0bf80ef9eeb59a5d2e6e3e1f", + me.WithClient(testClient), + me.WithDownloader(&mockDownloader{ + expected: expectedBlobContent, + shouldMatchLink: defaultLink, + }), + ) + }) + + It("downloads public spiff commit", func() { + spec := me.New("github.com", 0, "spiff", "mandelsoft", "25d9a3f0031c0b42e9ef7ab0117c35378040ef82") + + m, err := spec.AccessMethod(&cpi.DummyComponentVersionAccess{Context: ctx}) + Expect(err).ToNot(HaveOccurred()) + content, err := m.Get() + Expect(err).ToNot(HaveOccurred()) + Expect(len(content)).To(Equal(281655)) + }) + + if doPrivate { + Context("private access", func() { + It("downloads private commit", func() { + Configure(ctx) + + spec := me.New("github.com", 0, "cnudie-pause", "mandelsoft", "76eaae596ba24e401240654c4ad19ae66ba1e1a2") + + m, err := spec.AccessMethod(&cpi.DummyComponentVersionAccess{Context: ctx}) + Expect(err).ToNot(HaveOccurred()) + content, err := m.Get() + Expect(err).ToNot(HaveOccurred()) + Expect(len(content)).To(Equal(3764)) + }) + + It("downloads enterprise commit", func() { + Configure(ctx) + + spec := me.New("github.tools.sap", 0, "dummy", "D021770", "d17e2c594f0ab71f2c0f050b9d7fb485af4d6850") + + m, err := spec.AccessMethod(&cpi.DummyComponentVersionAccess{Context: ctx}) + Expect(err).ToNot(HaveOccurred()) + content, err := m.Get() + Expect(err).ToNot(HaveOccurred()) + Expect(len(content)).To(Equal(284)) + }) + }) + } + + It("downloads artifacts", func() { + m, err := accessSpec.AccessMethod(&cpi.DummyComponentVersionAccess{Context: ctx}) + Expect(err).ToNot(HaveOccurred()) + content, err := m.Get() + Expect(err).ToNot(HaveOccurred()) + Expect(content).To(Equal(expectedBlobContent)) + }) + + When("the commit sha is of an invalid length", func() { + It("errors", func() { + accessSpec := me.New( + "hostname", + 1234, + "repo", + "owner", + "not-a-sha", + me.WithClient(testClient), + me.WithDownloader(&mockDownloader{ + expected: expectedBlobContent, + shouldMatchLink: defaultLink, + }), + ) + m, err := accessSpec.AccessMethod(&cpi.DummyComponentVersionAccess{Context: ctx}) + Expect(err).ToNot(HaveOccurred()) + _, err = m.Get() + Expect(err).To(MatchError(ContainSubstring("commit is not a SHA"))) + }) + }) + + When("the commit sha is of the right length but contains invalid characters", func() { + It("errors", func() { + accessSpec := me.New( + "hostname", + 1234, + "repo", + "owner", + "refs/heads/veryinteresting_branch_namess", + me.WithClient(testClient), + me.WithDownloader(&mockDownloader{ + expected: expectedBlobContent, + shouldMatchLink: defaultLink, + }), + ) + m, err := accessSpec.AccessMethod(&cpi.DummyComponentVersionAccess{Context: ctx}) + Expect(err).ToNot(HaveOccurred()) + _, err = m.Get() + Expect(err).To(MatchError(ContainSubstring("commit contains invalid characters for a SHA"))) + }) + }) + + When("credentials are provided", func() { + It("can use those to access private repos", func() { + called := false + mcc := &mockCredContext{ + creds: &mockCredSource{ + cred: &mockCredentials{ + value: func() string { + called = true + return "test" + }, + }, + }, + } + m, err := accessSpec.AccessMethod(&mockComponentVersionAccess{ + credContext: mcc, + }) + Expect(err).ToNot(HaveOccurred()) + _, err = m.Get() + Expect(err).ToNot(HaveOccurred()) + Expect(called).To(BeTrue()) + }) + }) + + When("GetCredentialsForConsumer returns an error", func() { + It("errors", func() { + called := false + mcc := &mockCredContext{ + creds: &mockCredSource{ + cred: &mockCredentials{ + value: func() string { + called = true + return "test" + }, + }, + err: fmt.Errorf("danger will robinson"), + }, + } + _, err := accessSpec.AccessMethod(&mockComponentVersionAccess{ + credContext: mcc, + }) + Expect(err).To(MatchError(ContainSubstring("danger will robinson"))) + Expect(called).To(BeFalse()) + }) + }) +}) + +type mockComponentVersionAccess struct { + ocm.ComponentVersionAccess + credContext ocm.Context +} + +func (m *mockComponentVersionAccess) GetContext() ocm.Context { + return m.credContext +} + +type mockCredContext struct { + ocm.Context + creds credentials.Context +} + +func (m *mockCredContext) CredentialsContext() credentials.Context { + return m.creds +} + +type mockCredSource struct { + credentials.Context + cred credentials.Credentials + err error +} + +func (m *mockCredSource) GetCredentialsForConsumer(credentials.ConsumerIdentity, ...credentials.IdentityMatcher) (credentials.CredentialsSource, error) { + return m, m.err +} + +func (m *mockCredSource) Credentials(credentials.Context, ...credentials.CredentialsSource) (credentials.Credentials, error) { + return m.cred, nil +} + +type mockCredentials struct { + value func() string +} + +func (m *mockCredentials) Credentials(context core.Context, source ...core.CredentialsSource) (core.Credentials, error) { + panic("implement me") +} + +func (m *mockCredentials) ExistsProperty(name string) bool { + panic("implement me") +} + +func (m *mockCredentials) PropertyNames() sets.String { + panic("implement me") +} + +func (m *mockCredentials) Properties() common.Properties { + panic("implement me") +} + +func (m *mockCredentials) GetProperty(name string) string { + return m.value() +} diff --git a/pkg/contexts/ocm/accessmethods/github/suite_test.go b/pkg/contexts/ocm/accessmethods/github/suite_test.go new file mode 100644 index 000000000..be65d500e --- /dev/null +++ b/pkg/contexts/ocm/accessmethods/github/suite_test.go @@ -0,0 +1,27 @@ +// Copyright 2022 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package github_test + +import ( + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +func TestConfig(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Github Test Suite") +} diff --git a/pkg/contexts/ocm/accessmethods/github/testdata/repo.tar.gz b/pkg/contexts/ocm/accessmethods/github/testdata/repo.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..aec626b1b614cbb080592305f9b50951871ec926 GIT binary patch literal 272 zcmb2|=3oE==C@ZJ{SO(4uz#5QQ$%In@-44lceC@W6@6inZ`hK}_&CioyjOm{miS!8 z&ZZ+D{>Sdx;(aeW{{A+j^_yLV*JP%7=g;Xs`mon%?bqKkz8>GRf@`4)_cgE6TIac4 zgT%X)pL{x6Jl*GvVlqeO_3Ha;zng#epCEH>Vea4HU4|vLi{|W_d3#px)@dismijhn zrHcOU(vG$+YYu+S({5aAvhk(U&Mtm^wVy^m^``#5*RuL=(w?u(#<}nR|M|1?|I(Cc ztg_{=rPJ^I-@p2QN=