// InstallThirdPartyResource installs a third party resource specified by 'rsrc'. When a resource is // installed a corresponding RESTful resource is added as a valid path in the web service provided by // the master. // // For example, if you install a resource ThirdPartyResource{ Name: "foo.company.com", Versions: {"v1"} } // then the following RESTful resource is created on the server: // http://<host>/apis/company.com/v1/foos/... func (m *Master) InstallThirdPartyResource(rsrc *extensions.ThirdPartyResource) error { kind, group, err := thirdpartyresourcedata.ExtractApiGroupAndKind(rsrc) if err != nil { return err } plural, _ := meta.KindToResource(unversioned.GroupVersionKind{ Group: group, Version: rsrc.Versions[0].Name, Kind: kind, }) thirdparty := m.thirdpartyapi(group, kind, rsrc.Versions[0].Name, plural.Resource) if err := thirdparty.InstallREST(m.HandlerContainer); err != nil { glog.Fatalf("Unable to setup thirdparty api: %v", err) } path := makeThirdPartyPath(group) groupVersion := unversioned.GroupVersionForDiscovery{ GroupVersion: group + "/" + rsrc.Versions[0].Name, Version: rsrc.Versions[0].Name, } apiGroup := unversioned.APIGroup{ Name: group, Versions: []unversioned.GroupVersionForDiscovery{groupVersion}, PreferredVersion: groupVersion, } apiserver.AddGroupWebService(api.Codecs, m.HandlerContainer, path, apiGroup) m.addThirdPartyResourceStorage(path, thirdparty.Storage[plural.Resource].(*thirdpartyresourcedataetcd.REST), apiGroup) apiserver.InstallServiceErrorHandler(api.Codecs, m.HandlerContainer, m.NewRequestInfoResolver(), []string{thirdparty.GroupVersion.String()}) return nil }
func (f *FakeAPIInterface) HasThirdPartyResource(rsrc *expapi.ThirdPartyResource) (bool, error) { if f.apis == nil { return false, nil } _, group, _ := thirdpartyresourcedata.ExtractApiGroupAndKind(rsrc) path := makeThirdPartyPath(group) for _, api := range f.apis { if api == path { return true, nil } } return false, nil }
// HasThirdPartyResource returns true if a particular third party resource currently installed. func (m *Master) HasThirdPartyResource(rsrc *extensions.ThirdPartyResource) (bool, error) { _, group, err := thirdpartyresourcedata.ExtractApiGroupAndKind(rsrc) if err != nil { return false, err } path := makeThirdPartyPath(group) services := m.HandlerContainer.RegisteredWebServices() for ix := range services { if services[ix].RootPath() == path { return true, nil } } return false, nil }
func (m *Master) InstallThirdPartyAPI(rsrc *experimental.ThirdPartyResource) error { kind, group, err := thirdpartyresourcedata.ExtractApiGroupAndKind(rsrc) if err != nil { return err } thirdparty := m.thirdpartyapi(group, kind, rsrc.Versions[0].Name) if err := thirdparty.InstallREST(m.handlerContainer); err != nil { glog.Fatalf("Unable to setup thirdparty api: %v", err) } thirdPartyPrefix := "/thirdparty/" + group + "/" apiserver.AddApiWebService(m.handlerContainer, thirdPartyPrefix, []string{rsrc.Versions[0].Name}) thirdPartyRequestInfoResolver := &apiserver.APIRequestInfoResolver{APIPrefixes: sets.NewString(strings.TrimPrefix(group, "/")), RestMapper: thirdparty.Mapper} apiserver.InstallServiceErrorHandler(m.handlerContainer, thirdPartyRequestInfoResolver, []string{thirdparty.Version}) return nil }
func (t *ThirdPartyController) syncResourceList(list runtime.Object) error { existing := sets.String{} switch list := list.(type) { case *expapi.ThirdPartyResourceList: // Loop across all schema objects for third party resources for ix := range list.Items { item := &list.Items[ix] // extract the api group and resource kind from the schema _, group, err := thirdpartyresourcedata.ExtractApiGroupAndKind(item) if err != nil { return err } // place it in the set of resources that we expect, so that we don't delete it in the delete pass existing.Insert(makeThirdPartyPath(group)) // ensure a RESTful resource for this schema exists on the master if err := t.SyncOneResource(item); err != nil { return err } } default: return fmt.Errorf("expected a *ThirdPartyResourceList, got %#v", list) } // deletion phase, get all installed RESTful resources installed := t.master.ListThirdPartyResources() for _, installedAPI := range installed { found := false // search across the expected restful resources to see if this resource belongs to one of the expected ones for _, apiPath := range existing.List() { if installedAPI == apiPath || strings.HasPrefix(installedAPI, apiPath+"/") { found = true break } } // not expected, delete the resource if !found { if err := t.master.RemoveThirdPartyResource(installedAPI); err != nil { return err } } } return nil }
// HasThirdPartyResource returns true if a particular third party resource currently installed. func (m *Master) HasThirdPartyResource(rsrc *extensions.ThirdPartyResource) (bool, error) { kind, group, err := thirdpartyresourcedata.ExtractApiGroupAndKind(rsrc) if err != nil { return false, err } path := makeThirdPartyPath(group) m.thirdPartyResourcesLock.Lock() defer m.thirdPartyResourcesLock.Unlock() entry := m.thirdPartyResources[path] if entry == nil { return false, nil } plural, _ := meta.KindToResource(unversioned.GroupVersionKind{ Group: group, Version: rsrc.Versions[0].Name, Kind: kind, }) _, found := entry.storage[plural.Resource] return found, nil }
func TestInstallMultipleAPIs(t *testing.T) { names := []string{"foo.company.com", "bar.company.com"} versions := []string{"v1", "v1"} _, etcdserver, server, assert := initThirdPartyMultiple(t, versions, names) defer server.Close() defer etcdserver.Terminate(t) for ix := range names { kind, group, err := thirdpartyresourcedata.ExtractApiGroupAndKind( &extensions.ThirdPartyResource{ObjectMeta: api.ObjectMeta{Name: names[ix]}}) assert.NoError(err, "Failed to extract group & kind") plural, _ := meta.KindToResource(unversioned.GroupVersionKind{ Group: group, Version: versions[ix], Kind: kind, }) resp, err := http.Get( fmt.Sprintf("%s/apis/%s/%s/namespaces/default/%s", server.URL, group, versions[ix], plural.Resource)) if !assert.NoError(err, "Failed to do HTTP GET") { return } defer resp.Body.Close() assert.Equal(http.StatusOK, resp.StatusCode) data, err := ioutil.ReadAll(resp.Body) assert.NoError(err) obj := map[string]interface{}{} if err = json.Unmarshal(data, &obj); err != nil { assert.NoError(err, fmt.Sprintf("unexpected error: %v", err)) } kindOut, found := obj["kind"] if !found { t.Errorf("Missing 'kind' in %v", obj) } assert.Equal(kindOut, kind+"List") } }
// InstallThirdPartyResource installs a third party resource specified by 'rsrc'. When a resource is // installed a corresponding RESTful resource is added as a valid path in the web service provided by // the master. // // For example, if you install a resource ThirdPartyResource{ Name: "foo.company.com", Versions: {"v1"} } // then the following RESTful resource is created on the server: // http://<host>/apis/company.com/v1/foos/... func (m *Master) InstallThirdPartyResource(rsrc *expapi.ThirdPartyResource) error { kind, group, err := thirdpartyresourcedata.ExtractApiGroupAndKind(rsrc) if err != nil { return err } thirdparty := m.thirdpartyapi(group, kind, rsrc.Versions[0].Name) if err := thirdparty.InstallREST(m.handlerContainer); err != nil { glog.Fatalf("Unable to setup thirdparty api: %v", err) } path := makeThirdPartyPath(group) groupVersion := api.GroupVersion{ GroupVersion: group + "/" + rsrc.Versions[0].Name, Version: rsrc.Versions[0].Name, } apiGroup := api.APIGroup{ Name: group, Versions: []api.GroupVersion{groupVersion}, } apiserver.AddGroupWebService(m.handlerContainer, path, apiGroup) m.addThirdPartyResourceStorage(path, thirdparty.Storage[strings.ToLower(kind)+"s"].(*thirdpartyresourcedataetcd.REST)) apiserver.InstallServiceErrorHandler(m.handlerContainer, m.newAPIRequestInfoResolver(), []string{thirdparty.Version}) return nil }
func testInstallThirdPartyAPIListVersion(t *testing.T, version string) { tests := []struct { items []Foo name string test string }{ { name: "foo.company.com", test: "null", }, { items: []Foo{}, name: "foo.company.com", test: "empty", }, { items: []Foo{}, name: "policy.company.com", test: "plurals", }, { items: []Foo{ { ObjectMeta: api.ObjectMeta{ Name: "test", }, TypeMeta: unversioned.TypeMeta{ Kind: "Foo", APIVersion: version, }, SomeField: "test field", OtherField: 10, }, { ObjectMeta: api.ObjectMeta{ Name: "bar", }, TypeMeta: unversioned.TypeMeta{ Kind: "Foo", APIVersion: version, }, SomeField: "test field another", OtherField: 20, }, }, name: "foo.company.com", test: "real list", }, } for _, test := range tests { func() { master, etcdserver, server, assert := initThirdParty(t, version, test.name) defer server.Close() defer etcdserver.Terminate(t) kind, group, err := thirdpartyresourcedata.ExtractApiGroupAndKind( &extensions.ThirdPartyResource{ObjectMeta: api.ObjectMeta{Name: test.name}}) assert.NoError(err, test.test) plural, _ := meta.KindToResource(unversioned.GroupVersionKind{ Group: group, Version: version, Kind: kind, }) if test.items != nil { err := createThirdPartyList(master.thirdPartyStorage, fmt.Sprintf("/ThirdPartyResourceData/%s/%s/default", group, plural.Resource), test.items) if !assert.NoError(err, test.test) { return } } resp, err := http.Get( fmt.Sprintf("%s/apis/%s/%s/namespaces/default/%s", server.URL, group, version, plural.Resource)) if !assert.NoError(err, test.test) { return } defer resp.Body.Close() assert.Equal(http.StatusOK, resp.StatusCode, test.test) data, err := ioutil.ReadAll(resp.Body) assert.NoError(err, test.test) list := FooList{} if err = json.Unmarshal(data, &list); err != nil { assert.NoError(err, "unexpected error: %v %s", err, test.test) } if test.items == nil { if len(list.Items) != 0 { assert.NoError(err, "expected no items, saw: %v %s", err, list.Items, test.test) } return } if len(list.Items) != len(test.items) { t.Fatalf("(%s) unexpected length: %d vs %d", test.name, len(list.Items), len(test.items)) } // The order of elements in LIST is not guaranteed. mapping := make(map[string]int) for ix := range test.items { mapping[test.items[ix].Name] = ix } for ix := range list.Items { // Copy things that are set dynamically on the server expectedObj := test.items[mapping[list.Items[ix].Name]] expectedObj.SelfLink = list.Items[ix].SelfLink expectedObj.ResourceVersion = list.Items[ix].ResourceVersion expectedObj.Namespace = list.Items[ix].Namespace expectedObj.UID = list.Items[ix].UID expectedObj.CreationTimestamp = list.Items[ix].CreationTimestamp // We endure the order of items by sorting them (using 'mapping') // so that this function passes. if !reflect.DeepEqual(list.Items[ix], expectedObj) { t.Errorf("(%s) expected:\n%#v\nsaw:\n%#v\n", test.name, expectedObj, list.Items[ix]) } } }() } }
func (f *FakeAPIInterface) InstallThirdPartyResource(rsrc *expapi.ThirdPartyResource) error { f.installed = append(f.installed, rsrc) _, group, _ := thirdpartyresourcedata.ExtractApiGroupAndKind(rsrc) f.apis = append(f.apis, makeThirdPartyPath(group)) return nil }