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 }
// 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 *ThirdPartyResourceServer) InstallThirdPartyResource(rsrc *extensions.ThirdPartyResource) error { kind, group, err := thirdpartyresourcedata.ExtractApiGroupAndKind(rsrc) if err != nil { return err } if len(rsrc.Versions) == 0 { return fmt.Errorf("ThirdPartyResource %s has no defined versions", rsrc.Name) } plural, _ := meta.KindToResource(schema.GroupVersionKind{ Group: group, Version: rsrc.Versions[0].Name, Kind: kind, }) path := extensionsrest.MakeThirdPartyPath(group) groupVersion := metav1.GroupVersionForDiscovery{ GroupVersion: group + "/" + rsrc.Versions[0].Name, Version: rsrc.Versions[0].Name, } apiGroup := metav1.APIGroup{ Name: group, Versions: []metav1.GroupVersionForDiscovery{groupVersion}, PreferredVersion: groupVersion, } thirdparty := m.thirdpartyapi(group, kind, rsrc.Versions[0].Name, plural.Resource) // If storage exists, this group has already been added, just update // the group with the new API if m.hasThirdPartyGroupStorage(path) { m.addThirdPartyResourceStorage(path, plural.Resource, thirdparty.Storage[plural.Resource].(*thirdpartyresourcedatastore.REST), apiGroup) return thirdparty.UpdateREST(m.genericAPIServer.HandlerContainer.Container) } if err := thirdparty.InstallREST(m.genericAPIServer.HandlerContainer.Container); err != nil { glog.Errorf("Unable to setup thirdparty api: %v", err) } m.genericAPIServer.HandlerContainer.Add(genericapi.NewGroupWebService(api.Codecs, path, apiGroup)) m.addThirdPartyResourceStorage(path, plural.Resource, thirdparty.Storage[plural.Resource].(*thirdpartyresourcedatastore.REST), apiGroup) api.Registry.AddThirdPartyAPIGroupVersions(schema.GroupVersion{Group: group, Version: rsrc.Versions[0].Name}) return nil }
func (t *ThirdPartyController) syncResourceList(list runtime.Object) error { existing := sets.String{} switch list := list.(type) { case *extensions.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 *ThirdPartyResourceServer) HasThirdPartyResource(rsrc *extensions.ThirdPartyResource) (bool, error) { kind, group, err := thirdpartyresourcedata.ExtractApiGroupAndKind(rsrc) if err != nil { return false, err } path := extensionsrest.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") } }
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 }
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 { s, destroyFunc := generic.NewRawStorage(master.thirdPartyStorageConfig) defer destroyFunc() err := createThirdPartyList( s, 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]) } } }() } }