Signed-off-by: JimmyYang20 <yangjin39@huawei.com>tags/v0.2.0
| @@ -5,6 +5,7 @@ go 1.14 | |||||
| require ( | require ( | ||||
| github.com/emicklei/go-restful/v3 v3.4.0 | github.com/emicklei/go-restful/v3 v3.4.0 | ||||
| github.com/gorilla/websocket v1.4.0 | github.com/gorilla/websocket v1.4.0 | ||||
| github.com/minio/minio-go/v7 v7.0.10 | |||||
| github.com/onsi/ginkgo v1.11.0 | github.com/onsi/ginkgo v1.11.0 | ||||
| github.com/onsi/gomega v1.7.0 | github.com/onsi/gomega v1.7.0 | ||||
| github.com/spf13/cobra v1.0.0 | github.com/spf13/cobra v1.0.0 | ||||
| @@ -284,6 +284,7 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m | |||||
| github.com/googleapis/gnostic v0.4.1 h1:DLJCy1n/vrD4HPjOvYcT8aYQXpPIzoRZONaYwyycI+I= | github.com/googleapis/gnostic v0.4.1 h1:DLJCy1n/vrD4HPjOvYcT8aYQXpPIzoRZONaYwyycI+I= | ||||
| github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= | github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= | ||||
| github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= | github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= | ||||
| github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= | |||||
| github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= | ||||
| github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= | github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= | ||||
| github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= | github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= | ||||
| @@ -330,6 +331,7 @@ github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr | |||||
| github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= | ||||
| github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= | ||||
| github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= | ||||
| github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= | |||||
| github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= | ||||
| github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= | ||||
| github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= | github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= | ||||
| @@ -338,6 +340,9 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW | |||||
| github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= | github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= | ||||
| github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= | ||||
| github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= | github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= | ||||
| github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= | |||||
| github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s= | |||||
| github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4= | |||||
| github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= | ||||
| github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= | github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= | ||||
| github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= | github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= | ||||
| @@ -381,7 +386,14 @@ github.com/mholt/certmagic v0.6.2-0.20190624175158-6a42ef9fe8c2/go.mod h1:g4cOPx | |||||
| github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= | github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= | ||||
| github.com/miekg/dns v1.1.4/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= | github.com/miekg/dns v1.1.4/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= | ||||
| github.com/mindprince/gonvml v0.0.0-20190828220739-9ebdce4bb989/go.mod h1:2eu9pRWp8mo84xCg6KswZ+USQHjwgRhNp06sozOdsTY= | github.com/mindprince/gonvml v0.0.0-20190828220739-9ebdce4bb989/go.mod h1:2eu9pRWp8mo84xCg6KswZ+USQHjwgRhNp06sozOdsTY= | ||||
| github.com/minio/md5-simd v1.1.0 h1:QPfiOqlZH+Cj9teu0t9b1nTBfPbyTl16Of5MeuShdK4= | |||||
| github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw= | |||||
| github.com/minio/minio-go/v7 v7.0.10 h1:1oUKe4EOPUEhw2qnPQaPsJ0lmVTYLFu03SiItauXs94= | |||||
| github.com/minio/minio-go/v7 v7.0.10/go.mod h1:td4gW1ldOsj1PbSNS+WYK43j+P1XVhX/8W8awaYlBFo= | |||||
| github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU= | |||||
| github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= | |||||
| github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= | github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= | ||||
| github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= | |||||
| github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= | ||||
| github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= | github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= | ||||
| github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= | ||||
| @@ -465,6 +477,8 @@ github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uY | |||||
| github.com/robfig/cron v1.1.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= | github.com/robfig/cron v1.1.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= | ||||
| github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= | ||||
| github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= | ||||
| github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc= | |||||
| github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= | |||||
| github.com/rubiojr/go-vhd v0.0.0-20200706105327-02e210299021/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto= | github.com/rubiojr/go-vhd v0.0.0-20200706105327-02e210299021/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto= | ||||
| github.com/russross/blackfriday v0.0.0-20170610170232-067529f716f4/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= | github.com/russross/blackfriday v0.0.0-20170610170232-067529f716f4/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= | ||||
| github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= | github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= | ||||
| @@ -478,7 +492,9 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB | |||||
| github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= | ||||
| github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= | github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= | ||||
| github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= | github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= | ||||
| github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= | |||||
| github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= | ||||
| github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= | |||||
| github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= | github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= | ||||
| github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= | github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= | ||||
| github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= | ||||
| @@ -558,8 +574,9 @@ golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8U | |||||
| golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||
| golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||
| golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||||
| golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= | |||||
| golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||||
| golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 h1:DZhuSZLsGlFL4CmhA8BcRA0mnthyA/nZ00AqCUo7vHg= | |||||
| golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | |||||
| golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | ||||
| golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | ||||
| golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | ||||
| @@ -663,8 +680,9 @@ golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7w | |||||
| golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4 h1:5/PjkGUjvEU5Gl6BxmvKRPpqo2uNMv4rcHBMwzk/st8= | |||||
| golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae h1:Ih9Yo4hSPImZOpfGuA4bR/ORKTAbhZo2AbWNRCnevdo= | |||||
| golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |||||
| golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
| golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
| golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= | ||||
| @@ -771,6 +789,8 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy | |||||
| gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= | gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= | ||||
| gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= | gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= | ||||
| gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= | gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= | ||||
| gopkg.in/ini.v1 v1.57.0 h1:9unxIsFcTt4I55uWluz+UmL95q4kdJ0buvQ1ZIqVQww= | |||||
| gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= | |||||
| gopkg.in/mcuadros/go-syslog.v2 v2.2.1/go.mod h1:l5LPIyOOyIdQquNg+oU6Z3524YwrcqEm0aKH+5zpt2U= | gopkg.in/mcuadros/go-syslog.v2 v2.2.1/go.mod h1:l5LPIyOOyIdQquNg+oU6Z3524YwrcqEm0aKH+5zpt2U= | ||||
| gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= | gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= | ||||
| gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= | ||||
| @@ -22,6 +22,7 @@ import ( | |||||
| "fmt" | "fmt" | ||||
| "os" | "os" | ||||
| "path/filepath" | "path/filepath" | ||||
| "strings" | |||||
| "time" | "time" | ||||
| "k8s.io/klog/v2" | "k8s.io/klog/v2" | ||||
| @@ -30,6 +31,7 @@ import ( | |||||
| sednav1 "github.com/kubeedge/sedna/pkg/apis/sedna/v1alpha1" | sednav1 "github.com/kubeedge/sedna/pkg/apis/sedna/v1alpha1" | ||||
| "github.com/kubeedge/sedna/pkg/localcontroller/db" | "github.com/kubeedge/sedna/pkg/localcontroller/db" | ||||
| "github.com/kubeedge/sedna/pkg/localcontroller/gmclient" | "github.com/kubeedge/sedna/pkg/localcontroller/gmclient" | ||||
| "github.com/kubeedge/sedna/pkg/localcontroller/storage" | |||||
| "github.com/kubeedge/sedna/pkg/localcontroller/util" | "github.com/kubeedge/sedna/pkg/localcontroller/util" | ||||
| ) | ) | ||||
| @@ -50,8 +52,11 @@ type DatasetManager struct { | |||||
| // Dataset defines config for dataset | // Dataset defines config for dataset | ||||
| type Dataset struct { | type Dataset struct { | ||||
| *sednav1.Dataset | *sednav1.Dataset | ||||
| DataSource *DataSource `json:"dataSource"` | |||||
| Done chan struct{} | |||||
| DataSource *DataSource `json:"dataSource"` | |||||
| Done chan struct{} | |||||
| URLPrefix string | |||||
| Storage storage.Storage | |||||
| IsLocalStorage bool | |||||
| } | } | ||||
| // DatasetSpec defines dataset spec | // DatasetSpec defines dataset spec | ||||
| @@ -105,6 +110,14 @@ func (dm *DatasetManager) Insert(message *gmclient.Message) error { | |||||
| return err | return err | ||||
| } | } | ||||
| credential := dataset.ObjectMeta.Annotations[Credential] | |||||
| if credential != "" { | |||||
| dataset.Storage = storage.Storage{} | |||||
| if err := dataset.Storage.SetCredential(credential); err != nil { | |||||
| klog.Errorf("dataset(name=%s) sets storage credential failed, error: %+v", name, err) | |||||
| } | |||||
| } | |||||
| if first { | if first { | ||||
| go dm.monitorDataSources(name) | go dm.monitorDataSources(name) | ||||
| } | } | ||||
| @@ -140,11 +153,20 @@ func (dm *DatasetManager) monitorDataSources(name string) { | |||||
| return | return | ||||
| } | } | ||||
| if ds.Spec.URL == "" { | |||||
| klog.Errorf("dataset(name=%s) empty data source url.", name) | |||||
| dataURL := ds.Spec.URL | |||||
| prefix, err := storage.CheckURL(dataURL) | |||||
| if err != nil { | |||||
| klog.Errorf("dataset(name=%) has the invalid url, error: %+v", name, err) | |||||
| return | return | ||||
| } | } | ||||
| if prefix == storage.LocalPrefix { | |||||
| dataURL = util.AddPrefixPath(dm.VolumeMountPrefix, dataURL) | |||||
| ds.IsLocalStorage = true | |||||
| } | |||||
| ds.URLPrefix = strings.TrimRight(dataURL, filepath.Base(dataURL)) | |||||
| samplesNumber := 0 | |||||
| for { | for { | ||||
| select { | select { | ||||
| case <-ds.Done: | case <-ds.Done: | ||||
| @@ -152,16 +174,16 @@ func (dm *DatasetManager) monitorDataSources(name string) { | |||||
| default: | default: | ||||
| } | } | ||||
| dataURL := util.AddPrefixPath(dm.VolumeMountPrefix, filepath.Join(ds.Spec.URL)) | |||||
| dataSource, err := dm.getDataSource(dataURL, ds.Spec.Format) | |||||
| dataSource, err := ds.getDataSource(dataURL, ds.Spec.Format) | |||||
| if err != nil { | if err != nil { | ||||
| klog.Errorf("dataset(name=%s) get samples from %s failed", name, dataURL) | |||||
| klog.Errorf("dataset(name=%s) get samples from %s failed, error: %+v", name, dataURL, err) | |||||
| } else { | } else { | ||||
| ds.DataSource = dataSource | ds.DataSource = dataSource | ||||
| klog.Infof("dataset(name=%s) get samples from data source(url=%s) successfully. number of samples: %d", | |||||
| name, dataURL, dataSource.NumberOfSamples) | |||||
| if samplesNumber != dataSource.NumberOfSamples { | |||||
| samplesNumber = dataSource.NumberOfSamples | |||||
| klog.Infof("dataset(name=%s) get samples from data source(url=%s) successfully. number of samples: %d", | |||||
| name, dataURL, dataSource.NumberOfSamples) | |||||
| } | |||||
| header := gmclient.MessageHeader{ | header := gmclient.MessageHeader{ | ||||
| Namespace: ds.Namespace, | Namespace: ds.Namespace, | ||||
| ResourceKind: ds.Kind, | ResourceKind: ds.Kind, | ||||
| @@ -174,7 +196,7 @@ func (dm *DatasetManager) monitorDataSources(name string) { | |||||
| }{ | }{ | ||||
| dataSource.NumberOfSamples, | dataSource.NumberOfSamples, | ||||
| }, header); err != nil { | }, header); err != nil { | ||||
| klog.Errorf("dataset(name=%s) publish samples info failed", name) | |||||
| klog.Errorf("dataset(name=%s) publish samples info failed, error: %+v", name, err) | |||||
| } | } | ||||
| } | } | ||||
| <-time.After(MonitorDataSourceIntervalSeconds * time.Second) | <-time.After(MonitorDataSourceIntervalSeconds * time.Second) | ||||
| @@ -182,16 +204,21 @@ func (dm *DatasetManager) monitorDataSources(name string) { | |||||
| } | } | ||||
| // getDataSource gets data source info | // getDataSource gets data source info | ||||
| func (dm *DatasetManager) getDataSource(dataURL string, format string) (*DataSource, error) { | |||||
| func (ds *Dataset) getDataSource(dataURL string, format string) (*DataSource, error) { | |||||
| localURL, err := ds.Storage.Download(dataURL, "") | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| switch format { | switch format { | ||||
| case "txt": | case "txt": | ||||
| return dm.readByLine(dataURL) | |||||
| return ds.readByLine(localURL) | |||||
| } | } | ||||
| return nil, fmt.Errorf("not vaild file format") | return nil, fmt.Errorf("not vaild file format") | ||||
| } | } | ||||
| // readByLine reads file by line | // readByLine reads file by line | ||||
| func (dm *DatasetManager) readByLine(url string) (*DataSource, error) { | |||||
| func (ds *Dataset) readByLine(url string) (*DataSource, error) { | |||||
| samples, err := getSamples(url) | samples, err := getSamples(url) | ||||
| if err != nil { | if err != nil { | ||||
| klog.Errorf("read file %s failed, error: %v", url, err) | klog.Errorf("read file %s failed, error: %v", url, err) | ||||
| @@ -206,6 +233,12 @@ func (dm *DatasetManager) readByLine(url string) (*DataSource, error) { | |||||
| NumberOfSamples: numberOfSamples, | NumberOfSamples: numberOfSamples, | ||||
| } | } | ||||
| if !ds.IsLocalStorage { | |||||
| if err := os.Remove(url); err != nil { | |||||
| return nil, err | |||||
| } | |||||
| } | |||||
| return &dataSource, nil | return &dataSource, nil | ||||
| } | } | ||||
| @@ -23,6 +23,7 @@ import ( | |||||
| "os" | "os" | ||||
| "path" | "path" | ||||
| "strconv" | "strconv" | ||||
| "strings" | |||||
| "sync" | "sync" | ||||
| "time" | "time" | ||||
| @@ -32,6 +33,7 @@ import ( | |||||
| sednav1 "github.com/kubeedge/sedna/pkg/apis/sedna/v1alpha1" | sednav1 "github.com/kubeedge/sedna/pkg/apis/sedna/v1alpha1" | ||||
| "github.com/kubeedge/sedna/pkg/localcontroller/db" | "github.com/kubeedge/sedna/pkg/localcontroller/db" | ||||
| "github.com/kubeedge/sedna/pkg/localcontroller/gmclient" | "github.com/kubeedge/sedna/pkg/localcontroller/gmclient" | ||||
| "github.com/kubeedge/sedna/pkg/localcontroller/storage" | |||||
| "github.com/kubeedge/sedna/pkg/localcontroller/trigger" | "github.com/kubeedge/sedna/pkg/localcontroller/trigger" | ||||
| "github.com/kubeedge/sedna/pkg/localcontroller/util" | "github.com/kubeedge/sedna/pkg/localcontroller/util" | ||||
| ) | ) | ||||
| @@ -42,6 +44,7 @@ type IncrementalLearningJob struct { | |||||
| JobConfig *JobConfig | JobConfig *JobConfig | ||||
| Dataset *Dataset | Dataset *Dataset | ||||
| Done chan struct{} | Done chan struct{} | ||||
| Storage storage.Storage | |||||
| } | } | ||||
| // JobConfig defines config for incremental-learning-job | // JobConfig defines config for incremental-learning-job | ||||
| @@ -63,6 +66,7 @@ type JobConfig struct { | |||||
| DeployModel *ModelInfo | DeployModel *ModelInfo | ||||
| EvalResult []*ModelInfo | EvalResult []*ModelInfo | ||||
| Lock sync.Mutex | Lock sync.Mutex | ||||
| IsLocalStorage bool | |||||
| } | } | ||||
| // OutputConfig defines config for job output | // OutputConfig defines config for job output | ||||
| @@ -348,6 +352,14 @@ func (im *IncrementalJobManager) Insert(message *gmclient.Message) error { | |||||
| return err | return err | ||||
| } | } | ||||
| credential := job.ObjectMeta.Annotations[Credential] | |||||
| if credential != "" { | |||||
| job.Storage = storage.Storage{} | |||||
| if err := job.Storage.SetCredential(credential); err != nil { | |||||
| klog.Errorf("job(name=%s) sets storage credential failed, error: %+v", name, err) | |||||
| } | |||||
| } | |||||
| if first { | if first { | ||||
| go im.startJob(name) | go im.startJob(name) | ||||
| } | } | ||||
| @@ -379,7 +391,6 @@ func (im *IncrementalJobManager) Delete(message *gmclient.Message) error { | |||||
| // initJob inits the job object | // initJob inits the job object | ||||
| func (im *IncrementalJobManager) initJob(job *IncrementalLearningJob) error { | func (im *IncrementalJobManager) initJob(job *IncrementalLearningJob) error { | ||||
| jobConfig := job.JobConfig | jobConfig := job.JobConfig | ||||
| jobConfig.OutputDir = util.AddPrefixPath(im.VolumeMountPrefix, job.Spec.OutputDir) | |||||
| jobConfig.TrainModel = new(TrainModel) | jobConfig.TrainModel = new(TrainModel) | ||||
| jobConfig.TrainModel.OutputURL = jobConfig.OutputDir | jobConfig.TrainModel.OutputURL = jobConfig.OutputDir | ||||
| jobConfig.DeployModel = new(ModelInfo) | jobConfig.DeployModel = new(ModelInfo) | ||||
| @@ -400,6 +411,20 @@ func (im *IncrementalJobManager) initJob(job *IncrementalLearningJob) error { | |||||
| jobConfig.TrainTrigger = trainTrigger | jobConfig.TrainTrigger = trainTrigger | ||||
| jobConfig.DeployTrigger = deployTrigger | jobConfig.DeployTrigger = deployTrigger | ||||
| outputDir := job.Spec.OutputDir | |||||
| prefix, err := storage.CheckURL(outputDir) | |||||
| if err != nil { | |||||
| return fmt.Errorf("outout dir is unvalid, error: %+v", err) | |||||
| } | |||||
| jobConfig.IsLocalStorage = false | |||||
| jobConfig.OutputDir = outputDir | |||||
| if prefix == storage.LocalPrefix { | |||||
| jobConfig.IsLocalStorage = true | |||||
| jobConfig.OutputDir = util.AddPrefixPath(im.VolumeMountPrefix, outputDir) | |||||
| } | |||||
| if err := createOutputDir(jobConfig); err != nil { | if err := createOutputDir(jobConfig); err != nil { | ||||
| return err | return err | ||||
| } | } | ||||
| @@ -440,8 +465,9 @@ func (im *IncrementalJobManager) triggerTrainTask(job *IncrementalLearningJob) ( | |||||
| jobConfig.Version++ | jobConfig.Version++ | ||||
| jobConfig.TrainDataURL, err = im.writeSamples(jobConfig.DataSamples.TrainSamples, | |||||
| jobConfig.OutputConfig.SamplesOutput["train"], jobConfig.Version, job.Dataset.Spec.Format) | |||||
| var dataIndexURL string | |||||
| jobConfig.TrainDataURL, dataIndexURL, err = im.writeSamples(job, jobConfig.DataSamples.TrainSamples, | |||||
| jobConfig.OutputConfig.SamplesOutput["train"], jobConfig.Version, job.Dataset.Spec.Format, job.Dataset.URLPrefix) | |||||
| if err != nil { | if err != nil { | ||||
| klog.Errorf("train phase: write samples to the file(%s) is failed, error: %v", jobConfig.TrainDataURL, err) | klog.Errorf("train phase: write samples to the file(%s) is failed, error: %v", jobConfig.TrainDataURL, err) | ||||
| return nil, false, err | return nil, false, err | ||||
| @@ -452,11 +478,21 @@ func (im *IncrementalJobManager) triggerTrainTask(job *IncrementalLearningJob) ( | |||||
| Format: format, | Format: format, | ||||
| URL: jobConfig.TrainModel.TrainedModel[format], | URL: jobConfig.TrainModel.TrainedModel[format], | ||||
| } | } | ||||
| dataURL := jobConfig.TrainDataURL | |||||
| if jobConfig.IsLocalStorage { | |||||
| dataURL = util.TrimPrefixPath(im.VolumeMountPrefix, jobConfig.TrainDataURL) | |||||
| if !job.Dataset.IsLocalStorage { | |||||
| dataIndexURL = util.TrimPrefixPath(im.VolumeMountPrefix, dataIndexURL) | |||||
| } | |||||
| } | |||||
| input := WorkerInput{ | input := WorkerInput{ | ||||
| Models: []ModelInfo{m}, | |||||
| DataURL: util.TrimPrefixPath(im.VolumeMountPrefix, jobConfig.TrainDataURL), | |||||
| Models: []ModelInfo{m}, | |||||
| DataURL: dataURL, | |||||
| DataIndexURL: dataIndexURL, | |||||
| OutputDir: util.TrimPrefixPath(im.VolumeMountPrefix, | OutputDir: util.TrimPrefixPath(im.VolumeMountPrefix, | ||||
| path.Join(jobConfig.OutputConfig.TrainOutput, strconv.Itoa(jobConfig.Version))), | |||||
| strings.Join([]string{jobConfig.OutputConfig.TrainOutput, strconv.Itoa(jobConfig.Version)}, "/")), | |||||
| } | } | ||||
| msg := UpstreamMessage{ | msg := UpstreamMessage{ | ||||
| Phase: TrainPhase, | Phase: TrainPhase, | ||||
| @@ -472,8 +508,9 @@ func (im *IncrementalJobManager) triggerEvalTask(job *IncrementalLearningJob) (* | |||||
| jobConfig := job.JobConfig | jobConfig := job.JobConfig | ||||
| var err error | var err error | ||||
| (*jobConfig).EvalDataURL, err = im.writeSamples(jobConfig.DataSamples.EvalSamples, jobConfig.OutputConfig.SamplesOutput["eval"], | |||||
| jobConfig.Version, job.Dataset.Spec.Format) | |||||
| var dataIndexURL string | |||||
| jobConfig.EvalDataURL, dataIndexURL, err = im.writeSamples(job, jobConfig.DataSamples.EvalSamples, jobConfig.OutputConfig.SamplesOutput["eval"], | |||||
| jobConfig.Version, job.Dataset.Spec.Format, job.Dataset.URLPrefix) | |||||
| if err != nil { | if err != nil { | ||||
| klog.Errorf("job(name=%s) eval phase: write samples to the file(%s) is failed, error: %v", | klog.Errorf("job(name=%s) eval phase: write samples to the file(%s) is failed, error: %v", | ||||
| jobConfig.UniqueIdentifier, jobConfig.EvalDataURL, err) | jobConfig.UniqueIdentifier, jobConfig.EvalDataURL, err) | ||||
| @@ -491,11 +528,20 @@ func (im *IncrementalJobManager) triggerEvalTask(job *IncrementalLearningJob) (* | |||||
| URL: jobConfig.DeployModel.URL, | URL: jobConfig.DeployModel.URL, | ||||
| }) | }) | ||||
| dataURL := jobConfig.EvalDataURL | |||||
| if jobConfig.IsLocalStorage { | |||||
| dataURL = util.TrimPrefixPath(im.VolumeMountPrefix, jobConfig.EvalDataURL) | |||||
| if !job.Dataset.IsLocalStorage { | |||||
| dataIndexURL = util.TrimPrefixPath(im.VolumeMountPrefix, dataIndexURL) | |||||
| } | |||||
| } | |||||
| input := WorkerInput{ | input := WorkerInput{ | ||||
| Models: models, | |||||
| DataURL: util.TrimPrefixPath(im.VolumeMountPrefix, jobConfig.EvalDataURL), | |||||
| Models: models, | |||||
| DataURL: dataURL, | |||||
| DataIndexURL: dataIndexURL, | |||||
| OutputDir: util.TrimPrefixPath(im.VolumeMountPrefix, | OutputDir: util.TrimPrefixPath(im.VolumeMountPrefix, | ||||
| path.Join(jobConfig.OutputConfig.EvalOutput, strconv.Itoa(jobConfig.Version))), | |||||
| strings.Join([]string{jobConfig.OutputConfig.EvalOutput, strconv.Itoa(jobConfig.Version)}, "/")), | |||||
| } | } | ||||
| msg := &UpstreamMessage{ | msg := &UpstreamMessage{ | ||||
| Phase: EvalPhase, | Phase: EvalPhase, | ||||
| @@ -567,10 +613,8 @@ func (im *IncrementalJobManager) deployModel(job *IncrementalLearningJob) (*Mode | |||||
| trainedModel := util.AddPrefixPath(im.VolumeMountPrefix, models[0].URL) | trainedModel := util.AddPrefixPath(im.VolumeMountPrefix, models[0].URL) | ||||
| deployModel := util.AddPrefixPath(im.VolumeMountPrefix, models[1].URL) | deployModel := util.AddPrefixPath(im.VolumeMountPrefix, models[1].URL) | ||||
| if _, err = util.CopyFile(trainedModel, deployModel); err != nil { | |||||
| return nil, fmt.Errorf("failed to copy the trained model file(url=%s) to the deployment model file(url=%s): %v", | |||||
| trainedModel, deployModel, | |||||
| err) | |||||
| if err = job.updateDeployModel(deployModel, trainedModel); err != nil { | |||||
| return nil, err | |||||
| } | } | ||||
| jobConfig.DeployModel.Format = models[1].Format | jobConfig.DeployModel.Format = models[1].Format | ||||
| @@ -581,30 +625,43 @@ func (im *IncrementalJobManager) deployModel(job *IncrementalLearningJob) (*Mode | |||||
| return &models[0], nil | return &models[0], nil | ||||
| } | } | ||||
| func (job *IncrementalLearningJob) updateDeployModel(deployModel string, objectModel string) error { | |||||
| if _, err := job.Storage.Download(objectModel, deployModel); err != nil { | |||||
| return fmt.Errorf("copy model file(url=%s) to the deployment model file failed(url=%s): %v", | |||||
| objectModel, deployModel, err) | |||||
| } | |||||
| return nil | |||||
| } | |||||
| // createOutputDir creates the job output dir | // createOutputDir creates the job output dir | ||||
| func createOutputDir(jobConfig *JobConfig) error { | func createOutputDir(jobConfig *JobConfig) error { | ||||
| if err := util.CreateFolder(jobConfig.OutputDir); err != nil { | |||||
| klog.Errorf("job(name=%s) create fold %s failed", jobConfig.UniqueIdentifier, jobConfig.OutputDir) | |||||
| return err | |||||
| } | |||||
| outputDir := jobConfig.OutputDir | |||||
| dirNames := []string{"data/train", "data/eval", "train", "eval"} | dirNames := []string{"data/train", "data/eval", "train", "eval"} | ||||
| for _, v := range dirNames { | |||||
| dir := path.Join(jobConfig.OutputDir, v) | |||||
| if err := util.CreateFolder(dir); err != nil { | |||||
| klog.Errorf("job(name=%s) create fold %s failed", jobConfig.UniqueIdentifier, dir) | |||||
| if jobConfig.IsLocalStorage { | |||||
| if err := util.CreateFolder(outputDir); err != nil { | |||||
| klog.Errorf("job(name=%s) create fold %s failed", jobConfig.UniqueIdentifier, outputDir) | |||||
| return err | return err | ||||
| } | } | ||||
| for _, v := range dirNames { | |||||
| dir := path.Join(outputDir, v) | |||||
| if err := util.CreateFolder(dir); err != nil { | |||||
| klog.Errorf("job(name=%s) create fold %s failed", jobConfig.UniqueIdentifier, dir) | |||||
| return err | |||||
| } | |||||
| } | |||||
| } | } | ||||
| outputConfig := OutputConfig{ | outputConfig := OutputConfig{ | ||||
| SamplesOutput: map[string]string{ | SamplesOutput: map[string]string{ | ||||
| "train": path.Join(jobConfig.OutputDir, dirNames[0]), | |||||
| "eval": path.Join(jobConfig.OutputDir, dirNames[1]), | |||||
| "train": strings.Join([]string{strings.TrimRight(outputDir, "/"), dirNames[0]}, "/"), | |||||
| "eval": strings.Join([]string{strings.TrimRight(outputDir, "/"), dirNames[1]}, "/"), | |||||
| }, | }, | ||||
| TrainOutput: path.Join(jobConfig.OutputDir, dirNames[2]), | |||||
| EvalOutput: path.Join(jobConfig.OutputDir, dirNames[3]), | |||||
| TrainOutput: strings.Join([]string{strings.TrimRight(outputDir, "/"), dirNames[2]}, "/"), | |||||
| EvalOutput: strings.Join([]string{strings.TrimRight(outputDir, "/"), dirNames[3]}, "/"), | |||||
| } | } | ||||
| jobConfig.OutputConfig = &outputConfig | jobConfig.OutputConfig = &outputConfig | ||||
| @@ -735,32 +792,67 @@ func (im *IncrementalJobManager) handleData(job *IncrementalLearningJob) { | |||||
| } | } | ||||
| } | } | ||||
| // createFile creates a file | |||||
| func createFile(dir string, format string) (string, bool) { | |||||
| // createFile creates data file and data index file | |||||
| func createFile(dir string, format string, isLocalStorage bool) (string, string) { | |||||
| switch format { | switch format { | ||||
| case "txt": | case "txt": | ||||
| return path.Join(dir, "data.txt"), true | |||||
| if isLocalStorage { | |||||
| return path.Join(dir, "data.txt"), "" | |||||
| } | |||||
| return strings.Join([]string{dir, "data.txt"}, "/"), strings.Join([]string{dir, "dataIndex.txt"}, "/") | |||||
| } | } | ||||
| return "", false | |||||
| return "", "" | |||||
| } | } | ||||
| // writeSamples writes samples information to a file | // writeSamples writes samples information to a file | ||||
| func (im *IncrementalJobManager) writeSamples(samples []string, dir string, version int, format string) (string, error) { | |||||
| subDir := path.Join(dir, strconv.Itoa(version)) | |||||
| if err := util.CreateFolder(subDir); err != nil { | |||||
| return "", err | |||||
| } | |||||
| func (im *IncrementalJobManager) writeSamples(job *IncrementalLearningJob, samples []string, dir string, version int, format string, urlPrefix string) (string, string, error) { | |||||
| subDir := strings.Join([]string{dir, strconv.Itoa(version)}, "/") | |||||
| fileURL, absURLFile := createFile(subDir, format, job.Dataset.IsLocalStorage) | |||||
| fileURL, isFile := createFile(subDir, format) | |||||
| if isFile { | |||||
| if job.JobConfig.IsLocalStorage { | |||||
| if err := util.CreateFolder(subDir); err != nil { | |||||
| return "", "", err | |||||
| } | |||||
| if err := im.writeByLine(samples, fileURL); err != nil { | if err := im.writeByLine(samples, fileURL); err != nil { | ||||
| return "", err | |||||
| return "", "", err | |||||
| } | } | ||||
| } else { | |||||
| return "", fmt.Errorf("create a %s format file in %s failed", format, subDir) | |||||
| if !job.Dataset.IsLocalStorage { | |||||
| tempSamples := util.ParsingDatasetIndex(samples, urlPrefix) | |||||
| if err := im.writeByLine(tempSamples, absURLFile); err != nil { | |||||
| return "", "", err | |||||
| } | |||||
| } | |||||
| return fileURL, absURLFile, nil | |||||
| } | |||||
| temporaryDir, err := util.CreateTemporaryDir() | |||||
| if err != nil { | |||||
| return "", "", err | |||||
| } | |||||
| localFileURL, localAbsURLFile := createFile(temporaryDir, format, job.Dataset.IsLocalStorage) | |||||
| if err := im.writeByLine(samples, localFileURL); err != nil { | |||||
| return "", "", err | |||||
| } | |||||
| if err := job.Storage.Upload(localFileURL, fileURL); err != nil { | |||||
| return "", "", err | |||||
| } | |||||
| tempSamples := util.ParsingDatasetIndex(samples, urlPrefix) | |||||
| if err := im.writeByLine(tempSamples, localAbsURLFile); err != nil { | |||||
| return "", "", err | |||||
| } | |||||
| if err := job.Storage.Upload(localAbsURLFile, absURLFile); err != nil { | |||||
| return "", "", err | |||||
| } | } | ||||
| return fileURL, nil | |||||
| return fileURL, absURLFile, nil | |||||
| } | } | ||||
| // writeByLine writes file by line | // writeByLine writes file by line | ||||
| @@ -45,6 +45,9 @@ const ( | |||||
| TriggerReadyStatus = "ready" | TriggerReadyStatus = "ready" | ||||
| // TriggerCompletedStatus is the completed status about trigger in incremental-learning-job | // TriggerCompletedStatus is the completed status about trigger in incremental-learning-job | ||||
| TriggerCompletedStatus = "completed" | TriggerCompletedStatus = "completed" | ||||
| // Credential is credential of the storage service | |||||
| Credential = "sedna.io/credential" | |||||
| ) | ) | ||||
| // WorkerMessage defines message struct from worker | // WorkerMessage defines message struct from worker | ||||
| @@ -82,10 +85,10 @@ type UpstreamMessage struct { | |||||
| type WorkerInput struct { | type WorkerInput struct { | ||||
| // Only one model cases | // Only one model cases | ||||
| Models []ModelInfo `json:"models,omitempty"` | |||||
| DataURL string `json:"dataURL,omitempty"` | |||||
| OutputDir string `json:"outputDir,omitempty"` | |||||
| Models []ModelInfo `json:"models,omitempty"` | |||||
| DataURL string `json:"dataURL,omitempty"` | |||||
| DataIndexURL string `json:"dataIndexURL,omitempty"` | |||||
| OutputDir string `json:"outputDir,omitempty"` | |||||
| } | } | ||||
| // WorkerOutput defines output information of worker | // WorkerOutput defines output information of worker | ||||
| @@ -0,0 +1,147 @@ | |||||
| /* | |||||
| Copyright 2021 The KubeEdge Authors. | |||||
| 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 storage | |||||
| import ( | |||||
| "context" | |||||
| "fmt" | |||||
| "net/url" | |||||
| "strings" | |||||
| "time" | |||||
| "k8s.io/klog/v2" | |||||
| "github.com/kubeedge/sedna/pkg/localcontroller/util" | |||||
| "github.com/minio/minio-go/v7" | |||||
| "github.com/minio/minio-go/v7/pkg/credentials" | |||||
| ) | |||||
| // MinioClient defines a minio client | |||||
| type MinioClient struct { | |||||
| Client *minio.Client | |||||
| } | |||||
| // MaxTimeOut is max deadline time of client working | |||||
| const MaxTimeOut = 100 * time.Second | |||||
| // createMinioClient creates client | |||||
| func createMinioClient(endpoint string, useHTTPS string, accessKeyID string, secretAccessKey string) (*MinioClient, error) { | |||||
| token := "" | |||||
| useSSL := true | |||||
| if useHTTPS == "0" { | |||||
| useSSL = false | |||||
| } | |||||
| client, err := minio.New(endpoint, &minio.Options{ | |||||
| Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, token), | |||||
| Secure: useSSL, | |||||
| }) | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("initialize minio client failed, endpoint = %+v, error: %+v", endpoint, err) | |||||
| } | |||||
| c := MinioClient{ | |||||
| Client: client, | |||||
| } | |||||
| return &c, nil | |||||
| } | |||||
| // uploadFile uploads file from local host to storage service | |||||
| func (mc *MinioClient) uploadFile(localPath string, objectURL string) error { | |||||
| bucket, absPath, err := mc.parseURL(objectURL) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| if !util.IsExists(localPath) { | |||||
| return fmt.Errorf("file(%s) in the local host is not exists", localPath) | |||||
| } | |||||
| ctx, cancel := context.WithTimeout(context.Background(), MaxTimeOut) | |||||
| defer cancel() | |||||
| if _, err = mc.Client.FPutObject(ctx, bucket, absPath, localPath, minio.PutObjectOptions{}); err != nil { | |||||
| return fmt.Errorf("upload file from file path(%s) to file url(%s) failed, error: %+v", localPath, objectURL, err) | |||||
| } | |||||
| return nil | |||||
| } | |||||
| // copyFile copies file from the bucket to another bucket in storage service | |||||
| func (mc *MinioClient) copyFile(srcURL string, objectURL string) error { | |||||
| srcBucket, srcAbsPath, err := mc.parseURL(srcURL) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| srcOptions := minio.CopySrcOptions{ | |||||
| Bucket: srcBucket, | |||||
| Object: srcAbsPath, | |||||
| } | |||||
| objectBucket, objectAbsPath, err := mc.parseURL(objectURL) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| objectOptions := minio.CopyDestOptions{ | |||||
| Bucket: objectBucket, | |||||
| Object: objectAbsPath, | |||||
| } | |||||
| ctx, cancel := context.WithTimeout(context.Background(), MaxTimeOut) | |||||
| defer cancel() | |||||
| if _, err := mc.Client.CopyObject(ctx, objectOptions, srcOptions); err != nil { | |||||
| return fmt.Errorf("copy file from file url(%s) to file url(%s) failed, error: %+v", srcURL, objectURL, err) | |||||
| } | |||||
| return nil | |||||
| } | |||||
| // DownloadFile downloads file from storage service to the local host | |||||
| func (mc *MinioClient) downloadFile(objectURL string, localPath string) error { | |||||
| bucket, absPath, err := mc.parseURL(objectURL) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| ctx, cancel := context.WithTimeout(context.Background(), MaxTimeOut) | |||||
| defer cancel() | |||||
| if err = mc.Client.FGetObject(ctx, bucket, absPath, localPath, minio.GetObjectOptions{}); err != nil { | |||||
| return fmt.Errorf("download file from file url(%s) to file path(%s) failed, error: %+v", objectURL, localPath, err) | |||||
| } | |||||
| return nil | |||||
| } | |||||
| // parseURL parses url | |||||
| func (mc *MinioClient) parseURL(URL string) (string, string, error) { | |||||
| u, err := url.Parse(URL) | |||||
| if err != nil { | |||||
| return "", "", fmt.Errorf("invalid url(%s)", URL) | |||||
| } | |||||
| scheme := u.Scheme | |||||
| switch scheme { | |||||
| case S3Prefix: | |||||
| return u.Host, strings.TrimPrefix(u.Path, "/"), nil | |||||
| default: | |||||
| klog.Errorf("invalid scheme(%s)", scheme) | |||||
| } | |||||
| return "", "", fmt.Errorf("invalid url(%s)", URL) | |||||
| } | |||||
| @@ -0,0 +1,212 @@ | |||||
| /* | |||||
| Copyright 2021 The KubeEdge Authors. | |||||
| 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 storage | |||||
| import ( | |||||
| "encoding/json" | |||||
| "fmt" | |||||
| "net/url" | |||||
| "path" | |||||
| "path/filepath" | |||||
| "k8s.io/klog/v2" | |||||
| "github.com/kubeedge/sedna/pkg/localcontroller/util" | |||||
| ) | |||||
| const ( | |||||
| // S3Prefix defines that prefix of url is s3 | |||||
| S3Prefix = "s3" | |||||
| // LocalPrefix defines that prefix of url is local host | |||||
| LocalPrefix = "" | |||||
| // S3EndPoint is s3 endpoint of the storage service | |||||
| S3Endpoint = "s3-endpoint" | |||||
| // S3UseHTTPS determines whether to use HTTPS protocol | |||||
| S3UseHTTPS = "s3-usehttps" | |||||
| // AccessKeyId is access key id of the storage service | |||||
| AccessKeyID = "ACCESS_KEY_ID" | |||||
| // SecretAccessKey is secret access key of the storage service | |||||
| SecretAccessKey = "SECRET_ACCESS_KEY" | |||||
| ) | |||||
| type Storage struct { | |||||
| MinioClient *MinioClient | |||||
| } | |||||
| // Download downloads the file to the local host | |||||
| func (s *Storage) Download(objectURL string, localPath string) (string, error) { | |||||
| prefix, err := CheckURL(objectURL) | |||||
| if err != nil { | |||||
| return "", err | |||||
| } | |||||
| switch prefix { | |||||
| case LocalPrefix: | |||||
| return s.localCopy(objectURL, localPath) | |||||
| case S3Prefix: | |||||
| return s.downloadS3(objectURL, localPath) | |||||
| default: | |||||
| return "", fmt.Errorf("invalid url(%s)", objectURL) | |||||
| } | |||||
| } | |||||
| // downloadLocal copies the local file to another in local host | |||||
| func (s *Storage) localCopy(objectURL string, localPath string) (string, error) { | |||||
| if !util.IsExists(objectURL) { | |||||
| return "", fmt.Errorf("url(%s) is not exists", objectURL) | |||||
| } | |||||
| if localPath == "" { | |||||
| return objectURL, nil | |||||
| } | |||||
| dir := path.Dir(localPath) | |||||
| if !util.IsDir(dir) { | |||||
| util.CreateFolder(dir) | |||||
| } | |||||
| util.CopyFile(objectURL, localPath) | |||||
| return localPath, nil | |||||
| } | |||||
| // downloadS3 downloads the file from url of s3 to the local host | |||||
| func (s *Storage) downloadS3(objectURL string, localPath string) (string, error) { | |||||
| if localPath == "" { | |||||
| temporaryDir, err := util.CreateTemporaryDir() | |||||
| if err != nil { | |||||
| return "", err | |||||
| } | |||||
| localPath = path.Join(temporaryDir, filepath.Base(objectURL)) | |||||
| } | |||||
| dir := path.Dir(localPath) | |||||
| if !util.IsDir(dir) { | |||||
| util.CreateFolder(dir) | |||||
| } | |||||
| if err := s.MinioClient.downloadFile(objectURL, localPath); err != nil { | |||||
| return "", err | |||||
| } | |||||
| return localPath, nil | |||||
| } | |||||
| // checkMapKey checks whether key exists in the dict | |||||
| func checkMapKey(m map[string]string, key string) (string, error) { | |||||
| v, ok := m[key] | |||||
| if !ok { | |||||
| return "", fmt.Errorf("%s is empty", key) | |||||
| } | |||||
| return v, nil | |||||
| } | |||||
| // SetCredential sets credential of the storage service | |||||
| func (s *Storage) SetCredential(credential string) error { | |||||
| c := credential | |||||
| m := make(map[string]string) | |||||
| if err := json.Unmarshal([]byte(c), &m); err != nil { | |||||
| return err | |||||
| } | |||||
| endpoint, err := checkMapKey(m, S3Endpoint) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| useHTTPS, err := checkMapKey(m, S3UseHTTPS) | |||||
| if err != nil { | |||||
| useHTTPS = "1" | |||||
| } | |||||
| ak, err := checkMapKey(m, AccessKeyID) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| sk, err := checkMapKey(m, SecretAccessKey) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| mc, err := createMinioClient(endpoint, useHTTPS, ak, sk) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| s.MinioClient = mc | |||||
| return nil | |||||
| } | |||||
| // Upload uploads the file in local host to another(e.g., "s3") | |||||
| func (s *Storage) Upload(localPath string, objectURL string) error { | |||||
| prefix, err := CheckURL(objectURL) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| switch prefix { | |||||
| case S3Prefix: | |||||
| return s.uploadS3(localPath, objectURL) | |||||
| default: | |||||
| return fmt.Errorf("invalid url(%s)", objectURL) | |||||
| } | |||||
| } | |||||
| // uploadS3 uploads the file in local host to the url of s3 | |||||
| func (s *Storage) uploadS3(srcURL string, objectURL string) error { | |||||
| prefix, err := CheckURL(srcURL) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| switch prefix { | |||||
| case LocalPrefix: | |||||
| if err := s.MinioClient.uploadFile(srcURL, objectURL); err != nil { | |||||
| return err | |||||
| } | |||||
| case S3Prefix: | |||||
| if err := s.MinioClient.copyFile(srcURL, objectURL); err != nil { | |||||
| return err | |||||
| } | |||||
| } | |||||
| return nil | |||||
| } | |||||
| // CheckURL checks prefix of the url | |||||
| func CheckURL(objectURL string) (string, error) { | |||||
| if objectURL == "" { | |||||
| return "", fmt.Errorf("empty url") | |||||
| } | |||||
| u, err := url.Parse(objectURL) | |||||
| if err != nil { | |||||
| klog.Errorf("invalid url(%s), error: %v", objectURL, err) | |||||
| return "", fmt.Errorf("invalid url(%s)", objectURL) | |||||
| } | |||||
| l := []string{LocalPrefix, S3Prefix} | |||||
| for _, v := range l { | |||||
| if u.Scheme == v { | |||||
| return u.Scheme, nil | |||||
| } | |||||
| } | |||||
| return "", fmt.Errorf("unvalid url(%s), not support prefix(%s)", objectURL, u.Scheme) | |||||
| } | |||||
| @@ -19,9 +19,13 @@ package util | |||||
| import ( | import ( | ||||
| "fmt" | "fmt" | ||||
| "io" | "io" | ||||
| "math/rand" | |||||
| "os" | "os" | ||||
| "path" | |||||
| "path/filepath" | "path/filepath" | ||||
| "strconv" | |||||
| "strings" | "strings" | ||||
| "time" | |||||
| "k8s.io/klog/v2" | "k8s.io/klog/v2" | ||||
| ) | ) | ||||
| @@ -105,3 +109,23 @@ func AddPrefixPath(prefix string, path string) string { | |||||
| func GetUniqueIdentifier(namespace string, name string, kind string) string { | func GetUniqueIdentifier(namespace string, name string, kind string) string { | ||||
| return fmt.Sprintf("%s/%s/%s", namespace, kind, name) | return fmt.Sprintf("%s/%s/%s", namespace, kind, name) | ||||
| } | } | ||||
| // CreateTemporaryDir creates a temporary dir | |||||
| func CreateTemporaryDir() (string, error) { | |||||
| var src = rand.NewSource(time.Now().UnixNano()) | |||||
| dir := path.Join("/tmp/", strconv.FormatInt(src.Int63(), 10), "/") | |||||
| err := CreateFolder(dir) | |||||
| return dir, err | |||||
| } | |||||
| // ParsingDatasetIndex parses index file of dataset and adds the prefix to abs url of sample | |||||
| func ParsingDatasetIndex(samples []string, prefix string) []string { | |||||
| var l []string | |||||
| l = append(l, prefix) | |||||
| for _, v := range samples { | |||||
| absURL := strings.Split(v, " ")[0] | |||||
| l = append(l, absURL) | |||||
| } | |||||
| return l | |||||
| } | |||||