本文整理汇总了Golang中github.com/juju/juju/environs/tools.StorageName函数的典型用法代码示例。如果您正苦于以下问题:Golang StorageName函数的具体用法?Golang StorageName怎么用?Golang StorageName使用的例子?那么恭喜您, 这里精选的函数代码示例或许可以为您提供帮助。
在下文中一共展示了StorageName函数的20个代码示例,这些例子默认根据受欢迎程度排序。您可以为喜欢或者感觉有用的代码点赞,您的评价将有助于我们的系统推荐出更棒的Golang代码示例。
示例1: TestUploadSeriesExpanded
func (s *toolsSuite) TestUploadSeriesExpanded(c *gc.C) {
// Make some fake tools.
expectedTools, vers, toolPath := s.setupToolsForUpload(c)
// Now try uploading them. The "series" parameter is accepted
// but ignored; the API server will expand the tools for all
// supported series.
params := "?binaryVersion=" + vers.String() + "&series=nonsense"
resp, err := s.uploadRequest(c, s.toolsURI(c, params), true, toolPath)
c.Assert(err, gc.IsNil)
// Check the response.
stor := s.Environ.Storage()
toolsURL, err := stor.URL(tools.StorageName(vers))
c.Assert(err, gc.IsNil)
expectedTools[0].URL = toolsURL
s.assertUploadResponse(c, resp, expectedTools[0])
// Check the contents.
for _, series := range version.OSSupportedSeries(version.Ubuntu) {
toolsVersion := vers
toolsVersion.Series = series
r, err := stor.Get(tools.StorageName(toolsVersion))
c.Assert(err, gc.IsNil)
uploadedData, err := ioutil.ReadAll(r)
c.Assert(err, gc.IsNil)
expectedData, err := ioutil.ReadFile(toolPath)
c.Assert(err, gc.IsNil)
c.Assert(uploadedData, gc.DeepEquals, expectedData)
}
}
开发者ID:kapilt,项目名称:juju,代码行数:30,代码来源:tools_test.go
示例2: TestUploadFakeSeries
func (s *toolsSuite) TestUploadFakeSeries(c *gc.C) {
// Make some fake tools.
expectedTools, vers, toolPath := s.setupToolsForUpload(c)
// Now try uploading them.
params := "?binaryVersion=" + vers.String() + "&series=precise,trusty"
resp, err := s.uploadRequest(c, s.toolsURI(c, params), true, toolPath)
c.Assert(err, gc.IsNil)
// Check the response.
stor := s.Conn.Environ.Storage()
toolsURL, err := stor.URL(tools.StorageName(vers))
c.Assert(err, gc.IsNil)
expectedTools[0].URL = toolsURL
s.assertUploadResponse(c, resp, expectedTools[0])
// Check the contents.
for _, series := range []string{"precise", "quantal", "trusty"} {
toolsVersion := vers
toolsVersion.Series = series
r, err := stor.Get(tools.StorageName(toolsVersion))
c.Assert(err, gc.IsNil)
uploadedData, err := ioutil.ReadAll(r)
c.Assert(err, gc.IsNil)
expectedData, err := ioutil.ReadFile(toolPath)
c.Assert(err, gc.IsNil)
c.Assert(uploadedData, gc.DeepEquals, expectedData)
}
}
开发者ID:rogpeppe,项目名称:juju,代码行数:28,代码来源:tools_test.go
示例3: TestBootstrapWithDefaultSeries
func (t *LiveTests) TestBootstrapWithDefaultSeries(c *gc.C) {
if !t.HasProvisioner {
c.Skip("HasProvisioner is false; cannot test deployment")
}
current := version.Current
other := current
other.Series = "quantal"
if current == other {
other.Series = "precise"
}
dummyCfg, err := config.New(config.NoDefaults, dummy.SampleConfig().Merge(coretesting.Attrs{
"state-server": false,
"name": "dummy storage",
}))
dummyenv, err := environs.Prepare(dummyCfg, coretesting.Context(c), configstore.NewMem())
c.Assert(err, gc.IsNil)
defer dummyenv.Destroy()
t.Destroy(c)
attrs := t.TestConfig.Merge(coretesting.Attrs{"default-series": other.Series})
cfg, err := config.New(config.NoDefaults, attrs)
c.Assert(err, gc.IsNil)
env, err := environs.Prepare(cfg, coretesting.Context(c), t.ConfigStore)
c.Assert(err, gc.IsNil)
defer environs.Destroy(env, t.ConfigStore)
currentName := envtools.StorageName(current)
otherName := envtools.StorageName(other)
envStorage := env.Storage()
dummyStorage := dummyenv.Storage()
defer envStorage.Remove(otherName)
_, err = sync.Upload(dummyStorage, ¤t.Number)
c.Assert(err, gc.IsNil)
// This will only work while cross-compiling across releases is safe,
// which depends on external elements. Tends to be safe for the last
// few releases, but we may have to refactor some day.
err = storageCopy(dummyStorage, currentName, envStorage, otherName)
c.Assert(err, gc.IsNil)
err = bootstrap.Bootstrap(coretesting.Context(c), env, environs.BootstrapParams{})
c.Assert(err, gc.IsNil)
st := t.Env.(testing.GetStater).GetStateInAPIServer()
// Wait for machine agent to come up on the bootstrap
// machine and ensure it deployed the proper series.
m0, err := st.Machine("0")
c.Assert(err, gc.IsNil)
mw0 := newMachineToolWaiter(m0)
defer mw0.Stop()
waitAgentTools(c, mw0, other)
}
开发者ID:klyachin,项目名称:juju,代码行数:58,代码来源:livetests.go
示例4: populateTools
// populateTools stores uploaded tools in provider storage
// and updates the tools metadata.
//
// TODO(axw) store tools in gridfs, catalogue in state.
func (c *BootstrapCommand) populateTools(env environs.Environ) error {
agentConfig := c.CurrentConfig()
dataDir := agentConfig.DataDir()
tools, err := agenttools.ReadTools(dataDir, version.Current)
if err != nil {
return err
}
if !strings.HasPrefix(tools.URL, "file://") {
// Nothing to do since the tools were not uploaded.
return nil
}
// This is a hack: providers using localstorage (local, manual)
// can't use storage during bootstrap as the localstorage worker
// isn't running. Use filestorage instead.
var stor storage.Storage
storageDir := agentConfig.Value(agent.StorageDir)
if storageDir != "" {
stor, err = filestorage.NewFileStorageWriter(storageDir)
if err != nil {
return err
}
} else {
stor = env.Storage()
}
// Create a temporary directory to contain source and cloned tools.
tempDir, err := ioutil.TempDir("", "juju-sync-tools")
if err != nil {
return err
}
defer os.RemoveAll(tempDir)
destTools := filepath.Join(tempDir, filepath.FromSlash(envtools.StorageName(tools.Version)))
if err := os.MkdirAll(filepath.Dir(destTools), 0700); err != nil {
return err
}
srcTools := filepath.Join(
agenttools.SharedToolsDir(dataDir, version.Current),
"tools.tar.gz",
)
if err := utils.CopyFile(destTools, srcTools); err != nil {
return err
}
// Until we catalogue tools in state, we clone the tools
// for each of the supported series of the same OS.
otherSeries := version.OSSupportedSeries(version.Current.OS)
_, err = sync.SyncBuiltTools(stor, &sync.BuiltTools{
Version: tools.Version,
Dir: tempDir,
StorageName: envtools.StorageName(tools.Version),
Sha256Hash: tools.SHA256,
Size: tools.Size,
}, otherSeries...)
return err
}
开发者ID:kapilt,项目名称:juju,代码行数:60,代码来源:bootstrap.go
示例5: RemoveFakeTools
// RemoveFakeTools deletes the fake tools from the supplied storage.
func RemoveFakeTools(c *gc.C, stor storage.Storage, toolsDir string) {
c.Logf("removing fake tools")
toolsVersion := version.Current
name := envtools.StorageName(toolsVersion, toolsDir)
err := stor.Remove(name)
c.Check(err, jc.ErrorIsNil)
defaultSeries := coretesting.FakeDefaultSeries
if series.HostSeries() != defaultSeries {
toolsVersion.Series = defaultSeries
name := envtools.StorageName(toolsVersion, toolsDir)
err := stor.Remove(name)
c.Check(err, jc.ErrorIsNil)
}
RemoveFakeToolsMetadata(c, stor)
}
开发者ID:ktsakalozos,项目名称:juju,代码行数:16,代码来源:tools.go
示例6: RemoveFakeTools
// RemoveFakeTools deletes the fake tools from the supplied storage.
func RemoveFakeTools(c *gc.C, stor storage.Storage) {
c.Logf("removing fake tools")
toolsVersion := version.Current
name := envtools.StorageName(toolsVersion)
err := stor.Remove(name)
c.Check(err, gc.IsNil)
defaultSeries := coretesting.FakeDefaultSeries
if version.Current.Series != defaultSeries {
toolsVersion.Series = defaultSeries
name := envtools.StorageName(toolsVersion)
err := stor.Remove(name)
c.Check(err, gc.IsNil)
}
RemoveFakeToolsMetadata(c, stor)
}
开发者ID:kapilt,项目名称:juju,代码行数:16,代码来源:tools.go
示例7: TestMetadataFromTools
func (*metadataHelperSuite) TestMetadataFromTools(c *gc.C) {
metadata := tools.MetadataFromTools(nil, "proposed")
c.Assert(metadata, gc.HasLen, 0)
toolsList := coretools.List{{
Version: version.MustParseBinary("1.2.3-precise-amd64"),
Size: 123,
SHA256: "abc",
}, {
Version: version.MustParseBinary("2.0.1-raring-amd64"),
URL: "file:///tmp/proposed/juju-2.0.1-raring-amd64.tgz",
Size: 456,
SHA256: "xyz",
}}
metadata = tools.MetadataFromTools(toolsList, "proposed")
c.Assert(metadata, gc.HasLen, len(toolsList))
for i, t := range toolsList {
md := metadata[i]
c.Assert(md.Release, gc.Equals, t.Version.Series)
c.Assert(md.Version, gc.Equals, t.Version.Number.String())
c.Assert(md.Arch, gc.Equals, t.Version.Arch)
// FullPath is only filled out when reading tools using simplestreams.
// It's not needed elsewhere and requires a URL() call.
c.Assert(md.FullPath, gc.Equals, "")
c.Assert(md.Path, gc.Equals, tools.StorageName(t.Version, "proposed")[len("tools/"):])
c.Assert(md.FileType, gc.Equals, "tar.gz")
c.Assert(md.Size, gc.Equals, t.Size)
c.Assert(md.SHA256, gc.Equals, t.SHA256)
}
}
开发者ID:AlexisBruemmer,项目名称:juju,代码行数:30,代码来源:simplestreams_test.go
示例8: TestBootstrapToolsFileURL
func (s *bootstrapSuite) TestBootstrapToolsFileURL(c *gc.C) {
storageName := tools.StorageName(version.Current)
sftpURL, err := s.env.Storage().URL(storageName)
c.Assert(err, gc.IsNil)
fileURL := fmt.Sprintf("file://%s/%s", s.env.storageDir, storageName)
s.testBootstrapToolsURL(c, sftpURL, fileURL)
}
开发者ID:rogpeppe,项目名称:juju,代码行数:7,代码来源:bootstrap_test.go
示例9: setupToolsForUpload
func (s *toolsSuite) setupToolsForUpload(c *gc.C) (coretools.List, version.Binary, string) {
localStorage := c.MkDir()
vers := version.MustParseBinary("1.9.0-quantal-amd64")
versionStrings := []string{vers.String()}
expectedTools := toolstesting.MakeToolsWithCheckSum(c, localStorage, "releases", versionStrings)
toolsFile := tools.StorageName(vers)
return expectedTools, vers, path.Join(localStorage, toolsFile)
}
开发者ID:kapilt,项目名称:juju,代码行数:8,代码来源:tools_test.go
示例10: RemoveFakeTools
// RemoveFakeTools deletes the fake tools from the supplied storage.
func RemoveFakeTools(c *gc.C, stor storage.Storage, toolsDir string) {
c.Logf("removing fake tools")
toolsVersion := version.Binary{
Number: jujuversion.Current,
Arch: arch.HostArch(),
Series: series.HostSeries(),
}
name := envtools.StorageName(toolsVersion, toolsDir)
err := stor.Remove(name)
c.Check(err, jc.ErrorIsNil)
defaultSeries := series.LatestLts()
if series.HostSeries() != defaultSeries {
toolsVersion.Series = defaultSeries
name := envtools.StorageName(toolsVersion, toolsDir)
err := stor.Remove(name)
c.Check(err, jc.ErrorIsNil)
}
RemoveFakeToolsMetadata(c, stor)
}
开发者ID:bac,项目名称:juju,代码行数:20,代码来源:tools.go
示例11: TestUpgraderRetryAndChanged
func (s *UpgraderSuite) TestUpgraderRetryAndChanged(c *gc.C) {
stor := s.DefaultToolsStorage
oldTools := envtesting.PrimeTools(c, stor, s.DataDir(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.3-precise-amd64"))
s.PatchValue(&version.Current, oldTools.Version)
newTools := envtesting.AssertUploadFakeToolsVersions(
c, stor, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.5-precise-amd64"))[0]
err := statetesting.SetAgentVersion(s.State, newTools.Version.Number)
c.Assert(err, jc.ErrorIsNil)
retryc := make(chan time.Time)
*upgrader.RetryAfter = func() <-chan time.Time {
c.Logf("replacement retry after")
return retryc
}
err = stor.Remove(envtools.StorageName(newTools.Version, "released"))
c.Assert(err, jc.ErrorIsNil)
u := s.makeUpgrader(c)
defer u.Stop()
s.expectUpgradeChannelNotClosed(c)
for i := 0; i < 3; i++ {
select {
case retryc <- time.Now():
case <-time.After(coretesting.LongWait):
c.Fatalf("upgrader did not retry (attempt %d)", i)
}
}
// Make it upgrade to some newer tools that can be
// downloaded ok; it should stop retrying, download
// the newer tools and exit.
newerTools := envtesting.AssertUploadFakeToolsVersions(
c, stor, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.6-precise-amd64"))[0]
err = statetesting.SetAgentVersion(s.State, newerTools.Version.Number)
c.Assert(err, jc.ErrorIsNil)
s.BackingState.StartSync()
done := make(chan error)
go func() {
done <- u.Wait()
}()
select {
case err := <-done:
envtesting.CheckUpgraderReadyError(c, err, &upgrader.UpgradeReadyError{
AgentName: s.machine.Tag().String(),
OldTools: oldTools.Version,
NewTools: newerTools.Version,
DataDir: s.DataDir(),
})
case <-time.After(coretesting.LongWait):
c.Fatalf("upgrader did not quit after upgrading")
}
}
开发者ID:ktsakalozos,项目名称:juju,代码行数:54,代码来源:upgrader_test.go
示例12: UploadTools
func (u StorageToolsUploader) UploadTools(tools *coretools.Tools, data []byte) error {
toolsName := envtools.StorageName(tools.Version)
if err := u.Storage.Put(toolsName, bytes.NewReader(data), int64(len(data))); err != nil {
return err
}
err := envtools.MergeAndWriteMetadata(u.Storage, coretools.List{tools}, u.WriteMirrors)
if err != nil {
logger.Errorf("error writing tools metadata: %v", err)
return err
}
return nil
}
开发者ID:kapilt,项目名称:juju,代码行数:12,代码来源:sync.go
示例13: TestDownloadFetchesAndVerifiesSize
func (s *toolsSuite) TestDownloadFetchesAndVerifiesSize(c *gc.C) {
// Upload fake tools, then upload over the top so the SHA256 hash does not match.
stor := s.Environ.Storage()
envtesting.RemoveTools(c, stor)
tools := envtesting.AssertUploadFakeToolsVersions(c, stor, version.Current)[0]
err := stor.Put(envtools.StorageName(tools.Version), strings.NewReader("!"), 1)
resp, err := s.downloadRequest(c, tools.Version, "")
c.Assert(err, gc.IsNil)
s.assertErrorResponse(c, resp, http.StatusBadRequest, "error fetching tools: size mismatch for .*")
s.assertToolsNotStored(c, tools.Version)
}
开发者ID:zhouqt,项目名称:juju,代码行数:12,代码来源:tools_test.go
示例14: buildToolsTarball
// buildToolsTarball bundles a tools tarball and places it in a temp directory in
// the expected tools path.
func buildToolsTarball(forceVersion *version.Number) (builtTools *BuiltTools, err error) {
// TODO(rog) find binaries from $PATH when not using a development
// version of juju within a $GOPATH.
logger.Debugf("Building tools")
// We create the entire archive before asking the environment to
// start uploading so that we can be sure we have archived
// correctly.
f, err := ioutil.TempFile("", "juju-tgz")
if err != nil {
return nil, err
}
defer f.Close()
defer os.Remove(f.Name())
toolsVersion, sha256Hash, err := envtools.BundleTools(f, forceVersion)
if err != nil {
return nil, err
}
fileInfo, err := f.Stat()
if err != nil {
return nil, fmt.Errorf("cannot stat newly made tools archive: %v", err)
}
size := fileInfo.Size()
logger.Infof("built tools %v (%dkB)", toolsVersion, (size+512)/1024)
baseToolsDir, err := ioutil.TempDir("", "juju-tools")
if err != nil {
return nil, err
}
// If we exit with an error, clean up the built tools directory.
defer func() {
if err != nil {
os.RemoveAll(baseToolsDir)
}
}()
err = os.MkdirAll(filepath.Join(baseToolsDir, storage.BaseToolsPath, "releases"), 0755)
if err != nil {
return nil, err
}
storageName := envtools.StorageName(toolsVersion)
err = utils.CopyFile(filepath.Join(baseToolsDir, storageName), f.Name())
if err != nil {
return nil, err
}
return &BuiltTools{
Version: toolsVersion,
Dir: baseToolsDir,
StorageName: storageName,
Size: size,
Sha256Hash: sha256Hash,
}, nil
}
开发者ID:jiasir,项目名称:juju,代码行数:55,代码来源:sync.go
示例15: TestDownloadFetchesAndVerifiesSize
func (s *toolsSuite) TestDownloadFetchesAndVerifiesSize(c *gc.C) {
// Upload fake tools, then upload over the top so the SHA256 hash does not match.
s.PatchValue(&version.Current.Number, testing.FakeVersionNumber)
stor := s.DefaultToolsStorage
envtesting.RemoveTools(c, stor, "released")
tools := envtesting.AssertUploadFakeToolsVersions(c, stor, "released", "released", version.Current)[0]
err := stor.Put(envtools.StorageName(tools.Version, "released"), strings.NewReader("!"), 1)
c.Assert(err, jc.ErrorIsNil)
resp, err := s.downloadRequest(c, tools.Version, "")
c.Assert(err, jc.ErrorIsNil)
s.assertErrorResponse(c, resp, http.StatusBadRequest, "error fetching tools: size mismatch for .*")
s.assertToolsNotStored(c, tools.Version)
}
开发者ID:kakamessi99,项目名称:juju,代码行数:14,代码来源:tools_test.go
示例16: cloneToolsForSeries
// cloneToolsForSeries copies the built tools tarball into a tarball for the specified
// stream and series and generates corresponding metadata.
func cloneToolsForSeries(toolsInfo *BuiltTools, stream string, series ...string) error {
// Copy the tools to the target storage, recording a Tools struct for each one.
var targetTools coretools.List
targetTools = append(targetTools, &coretools.Tools{
Version: toolsInfo.Version,
Size: toolsInfo.Size,
SHA256: toolsInfo.Sha256Hash,
})
putTools := func(vers version.Binary) (string, error) {
name := envtools.StorageName(vers, stream)
src := filepath.Join(toolsInfo.Dir, toolsInfo.StorageName)
dest := filepath.Join(toolsInfo.Dir, name)
destDir := filepath.Dir(dest)
if err := os.MkdirAll(destDir, 0755); err != nil {
return "", err
}
if err := utils.CopyFile(dest, src); err != nil {
return "", err
}
// Append to targetTools the attributes required to write out tools metadata.
targetTools = append(targetTools, &coretools.Tools{
Version: vers,
Size: toolsInfo.Size,
SHA256: toolsInfo.Sha256Hash,
})
return name, nil
}
logger.Debugf("generating tarballs for %v", series)
for _, series := range series {
_, err := jujuseries.SeriesVersion(series)
if err != nil {
return err
}
if series != toolsInfo.Version.Series {
fakeVersion := toolsInfo.Version
fakeVersion.Series = series
if _, err := putTools(fakeVersion); err != nil {
return err
}
}
}
// The tools have been copied to a temp location from which they will be uploaded,
// now write out the matching simplestreams metadata so that SyncTools can find them.
metadataStore, err := filestorage.NewFileStorageWriter(toolsInfo.Dir)
if err != nil {
return err
}
logger.Debugf("generating tools metadata")
return envtools.MergeAndWriteMetadata(metadataStore, stream, stream, targetTools, false)
}
开发者ID:AlexisBruemmer,项目名称:juju,代码行数:52,代码来源:sync.go
示例17: uploadFakeToolsVersion
func uploadFakeToolsVersion(stor storage.Storage, toolsDir string, vers version.Binary) (*coretools.Tools, error) {
logger.Infof("uploading FAKE tools %s", vers)
tgz, checksum := makeFakeTools(vers)
size := int64(len(tgz))
name := envtools.StorageName(vers, toolsDir)
if err := stor.Put(name, bytes.NewReader(tgz), size); err != nil {
return nil, err
}
url, err := stor.URL(name)
if err != nil {
return nil, err
}
return &coretools.Tools{URL: url, Version: vers, Size: size, SHA256: checksum}, nil
}
开发者ID:pmatulis,项目名称:juju,代码行数:14,代码来源:tools.go
示例18: uploadFakeToolsVersion
func uploadFakeToolsVersion(stor storage.Storage, vers version.Binary) (*coretools.Tools, error) {
logger.Infof("uploading FAKE tools %s", vers)
tgz, checksum := coretesting.TarGz(
coretesting.NewTarFile("jujud", 0777, "jujud contents "+vers.String()))
size := int64(len(tgz))
name := envtools.StorageName(vers)
if err := stor.Put(name, bytes.NewReader(tgz), size); err != nil {
return nil, err
}
url, err := stor.URL(name)
if err != nil {
return nil, err
}
return &coretools.Tools{URL: url, Version: vers, Size: size, SHA256: checksum}, nil
}
开发者ID:jiasir,项目名称:juju,代码行数:15,代码来源:tools.go
示例19: TestUploadAllowsTopLevelPath
func (s *toolsSuite) TestUploadAllowsTopLevelPath(c *gc.C) {
// Backwards compatibility check, that we can upload tools to
// https://host:port/tools
expectedTools, vers, toolPath := s.setupToolsForUpload(c)
url := s.toolsURL(c, "binaryVersion="+vers.String())
url.Path = "/tools"
resp, err := s.uploadRequest(c, url.String(), true, toolPath)
c.Assert(err, gc.IsNil)
// Check the response.
stor := s.Environ.Storage()
toolsURL, err := stor.URL(tools.StorageName(vers))
c.Assert(err, gc.IsNil)
expectedTools[0].URL = toolsURL
s.assertUploadResponse(c, resp, expectedTools[0])
}
开发者ID:kapilt,项目名称:juju,代码行数:15,代码来源:tools_test.go
示例20: TestUpload
func (s *toolsSuite) TestUpload(c *gc.C) {
// Make some fake tools.
expectedTools, vers, toolPath := s.setupToolsForUpload(c)
// Now try uploading them.
resp, err := s.uploadRequest(
c, s.toolsURI(c, "?binaryVersion="+vers.String()), true, toolPath)
c.Assert(err, gc.IsNil)
// Check the response.
stor := s.Environ.Storage()
toolsURL, err := stor.URL(tools.StorageName(vers))
c.Assert(err, gc.IsNil)
expectedTools[0].URL = toolsURL
s.assertUploadResponse(c, resp, expectedTools[0])
// Check the contents.
r, err := stor.Get(tools.StorageName(vers))
c.Assert(err, gc.IsNil)
uploadedData, err := ioutil.ReadAll(r)
c.Assert(err, gc.IsNil)
expectedData, err := ioutil.ReadFile(toolPath)
c.Assert(err, gc.IsNil)
c.Assert(uploadedData, gc.DeepEquals, expectedData)
}
开发者ID:kapilt,项目名称:juju,代码行数:24,代码来源:tools_test.go
注:本文中的github.com/juju/juju/environs/tools.StorageName函数示例整理自Github/MSDocs等源码及文档管理平台,相关代码片段筛选自各路编程大神贡献的开源项目,源码版权归原作者所有,传播和使用请参考对应项目的License;未经允许,请勿转载。 |
请发表评论